├── .editorconfig ├── .firebaserc ├── .gitignore ├── LICENSE.md ├── README.md ├── angular.json ├── browserslist ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── firebase.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── comments │ │ ├── comments.component.css │ │ ├── comments.component.html │ │ ├── comments.component.spec.ts │ │ └── comments.component.ts │ ├── create-post │ │ ├── create-post.component.css │ │ ├── create-post.component.html │ │ ├── create-post.component.spec.ts │ │ └── create-post.component.ts │ ├── feed-redirect │ │ ├── feed-redirect.component.css │ │ ├── feed-redirect.component.html │ │ ├── feed-redirect.component.spec.ts │ │ └── feed-redirect.component.ts │ ├── filter-posts │ │ ├── filter-posts.component.css │ │ ├── filter-posts.component.html │ │ ├── filter-posts.component.spec.ts │ │ └── filter-posts.component.ts │ ├── footer │ │ ├── footer.component.css │ │ ├── footer.component.html │ │ ├── footer.component.spec.ts │ │ └── footer.component.ts │ ├── full-post │ │ ├── full-post.component.css │ │ ├── full-post.component.html │ │ ├── full-post.component.spec.ts │ │ └── full-post.component.ts │ ├── home │ │ ├── home.component.css │ │ ├── home.component.html │ │ ├── home.component.spec.ts │ │ └── home.component.ts │ ├── info │ │ ├── about-us │ │ │ ├── about-us.component.css │ │ │ ├── about-us.component.html │ │ │ ├── about-us.component.spec.ts │ │ │ └── about-us.component.ts │ │ ├── contact-us │ │ │ ├── contact-us.component.css │ │ │ ├── contact-us.component.html │ │ │ ├── contact-us.component.spec.ts │ │ │ └── contact-us.component.ts │ │ └── meet-the-team │ │ │ ├── meet-the-team.component.css │ │ │ ├── meet-the-team.component.html │ │ │ ├── meet-the-team.component.spec.ts │ │ │ └── meet-the-team.component.ts │ ├── loader │ │ ├── loader.component.css │ │ ├── loader.component.html │ │ ├── loader.component.spec.ts │ │ └── loader.component.ts │ ├── main-navbar │ │ ├── main-navbar.component.css │ │ ├── main-navbar.component.html │ │ ├── main-navbar.component.spec.ts │ │ └── main-navbar.component.ts │ ├── models │ │ ├── comment.ts │ │ ├── feedback.ts │ │ └── post.ts │ ├── not-found │ │ ├── not-found.component.css │ │ ├── not-found.component.html │ │ ├── not-found.component.spec.ts │ │ └── not-found.component.ts │ ├── pipes │ │ ├── search-highlight.pipe.spec.ts │ │ └── search-highlight.pipe.ts │ ├── popular │ │ ├── popular.component.css │ │ ├── popular.component.html │ │ ├── popular.component.spec.ts │ │ └── popular.component.ts │ ├── post-card │ │ ├── post-card.component.css │ │ ├── post-card.component.html │ │ ├── post-card.component.spec.ts │ │ └── post-card.component.ts │ ├── posts-filter │ │ ├── posts-filter.component.css │ │ ├── posts-filter.component.html │ │ ├── posts-filter.component.spec.ts │ │ └── posts-filter.component.ts │ ├── posts │ │ ├── posts.component.css │ │ ├── posts.component.html │ │ ├── posts.component.spec.ts │ │ └── posts.component.ts │ ├── privacy-policy │ │ ├── privacy-policy.component.css │ │ ├── privacy-policy.component.html │ │ ├── privacy-policy.component.spec.ts │ │ └── privacy-policy.component.ts │ ├── recent │ │ ├── recent.component.css │ │ ├── recent.component.html │ │ ├── recent.component.spec.ts │ │ └── recent.component.ts │ ├── search │ │ ├── search.component.css │ │ ├── search.component.html │ │ ├── search.component.spec.ts │ │ └── search.component.ts │ ├── services │ │ ├── admin-authguard.service.spec.ts │ │ ├── admin-authguard.service.ts │ │ ├── analytics.service.spec.ts │ │ ├── analytics.service.ts │ │ ├── auth-service.service.spec.ts │ │ ├── auth-service.service.ts │ │ ├── feedback-service.service.spec.ts │ │ ├── feedback-service.service.ts │ │ ├── post.service.spec.ts │ │ ├── post.service.ts │ │ ├── tag.service.spec.ts │ │ └── tag.service.ts │ ├── sharing-options │ │ ├── sharing-options.component.css │ │ ├── sharing-options.component.html │ │ ├── sharing-options.component.spec.ts │ │ └── sharing-options.component.ts │ ├── spinner │ │ ├── spinner.component.css │ │ ├── spinner.component.html │ │ ├── spinner.component.spec.ts │ │ └── spinner.component.ts │ └── trending │ │ ├── trending.component.css │ │ ├── trending.component.html │ │ ├── trending.component.spec.ts │ │ └── trending.component.ts ├── assets │ ├── .gitkeep │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── feed.png │ ├── home.png │ ├── img │ │ ├── IMG-subham.jpg │ │ ├── IMG-subhranil.jpg │ │ ├── github-light.png │ │ ├── icon-features.png │ │ ├── icon-secure.png │ │ ├── icon-trending.png │ │ ├── icons │ │ │ ├── facebook.png │ │ │ ├── gmail.png │ │ │ ├── linkedin.png │ │ │ ├── twitter.png │ │ │ └── whatsapp.png │ │ ├── logo │ │ │ ├── logo-body-del.jpg │ │ │ ├── logo-body.jpg │ │ │ ├── logo-navbar-del.png │ │ │ └── logo-navbar.png │ │ ├── self-image-circle-min-Subham.png │ │ └── self-image-circle.png │ ├── mstile-150x150.png │ ├── search.png │ ├── site.webmanifest │ ├── smtp.js │ └── version-history.txt ├── custom-theme.scss ├── favicon │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── mstile-150x150.png │ └── site.webmanifest ├── index.html ├── main.ts ├── polyfills.ts ├── styles.css └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "kratos-e41c9" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | #firebase cache 14 | /.firebase 15 | 16 | # keys 17 | /src/environments 18 | 19 | #admin 20 | /src/app/admin 21 | 22 | #mailing details 23 | /src/app/services/mailing.service.ts 24 | /src/app/services/mailing.service.spec.ts 25 | 26 | # assets 27 | /logo-assets 28 | 29 | # profiling files 30 | chrome-profiler-events*.json 31 | speed-measure-plugin*.json 32 | 33 | # IDEs and editors 34 | /.idea 35 | .project 36 | .classpath 37 | .c9/ 38 | *.launch 39 | .settings/ 40 | *.sublime-workspace 41 | 42 | # IDE - VSCode 43 | .vscode/* 44 | !.vscode/settings.json 45 | !.vscode/tasks.json 46 | !.vscode/launch.json 47 | !.vscode/extensions.json 48 | .history/* 49 | 50 | # misc 51 | /.sass-cache 52 | /connect.lock 53 | /coverage 54 | /libpeerconnection.log 55 | npm-debug.log 56 | yarn-error.log 57 | testem.log 58 | /typings 59 | 60 | # System Files 61 | .DS_Store 62 | Thumbs.db 63 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2021 Subhranil Dutta, Subham Das 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloakspace 2 | 3 | Image of Cloakspace 4 | 5 | An anonymous platform for everyone to share their thoughts freely without the fear of being tracked or the prying eyes of social media. 6 | 7 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT "MIT License") 8 | ![GitHub repo size](https://img.shields.io/github/repo-size/das-jishu/cloakspace) 9 | [![Active](http://img.shields.io/badge/Status-Active-green.svg)](https://github.com/das-jishu/simplifying) 10 | [![Generic badge](https://img.shields.io/badge/lang-typescript-yellow.svg)](https://www.typescriptlang.org/) 11 | [![Generic badge](https://img.shields.io/badge/framework-angular-blue.svg)](https://angular.io/) 12 | [![Generic badge](https://img.shields.io/badge/hosting-firebase-green.svg)](https://firebase.google.com/) 13 | 14 |
15 | 16 | ## ABOUT US 17 | 18 | [Cloakspace](https://cloakspace.tech) is different from a typical social media platform which logs all sorts of data about the user. We do not store any of your data and simultaneously provide a smooth experience. We tried to provide all sorts of features which can be exploited without storing information about the user. There are no hidden costs involved. This is a completely free platform where you can leave your thoughts and read some more without constantly worrying of being tracked or judged by someone. 19 | 20 |
21 | 22 | ## FEATURES 23 | 24 | - Posting anonymously without sharing any private information. 25 | - Reacting to posts, commenting, sharing and reporting any post if found inappropriate. 26 | 27 | Image of feeds 28 | 29 | - Replying to comments. 30 | - Searching based on date, tags or content. 31 | - Advanced search highlighting. 32 | 33 | Advanced search highlighting 34 | 35 | - Real time updates. 36 | - Data Security and anonymity when posting. 37 | - Notifications via mail when a new post arrives or when someone comments on the specific user's posts. 38 | - No ads or promotions cropping up. 39 | - Strict policy to maintain the quality of the posts. 40 | - A constructive feedback system. 41 | 42 | #### Visit [Cloakspace](https://cloakspace.tech) now! :heart: 43 | 44 | #### Follow us on [Facebook](https://www.facebook.com/cloakspace07/) to get updates! :smiley: 45 | 46 | #### Also check out our post on [Medium](https://medium.com/@das.jishu25/need-anonymity-and-privacy-to-feel-safe-when-opening-up-cloakspace-is-here-for-you-a11bfc0c67da?source=your_stories_page---------------------------). 47 | 48 | If you loved our project, do leave a star to show us your support. Thank you! :star: 49 | 50 | ### LICENSE 51 | 52 | [MIT](https://github.com/das-jishu/cloakspace/blob/master/LICENSE.md) 53 | 54 | ### Authors 55 | 56 | [Subhranil Dutta](https://github.com/alpha037) 57 |
58 | [Subham Das](https://github.com/das-jishu) 59 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "kratos": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/kratos", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": true, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "node_modules/bootstrap/dist/css/bootstrap.min.css", 28 | "src/styles.css", 29 | "src/custom-theme.scss", 30 | "node_modules/@ctrl/ngx-emoji-mart/picker.css" 31 | ], 32 | "scripts": [] 33 | }, 34 | "configurations": { 35 | "production": { 36 | "fileReplacements": [ 37 | { 38 | "replace": "src/environments/environment.ts", 39 | "with": "src/environments/environment.prod.ts" 40 | } 41 | ], 42 | "optimization": true, 43 | "outputHashing": "all", 44 | "sourceMap": false, 45 | "extractCss": true, 46 | "namedChunks": false, 47 | "extractLicenses": true, 48 | "vendorChunk": false, 49 | "buildOptimizer": true, 50 | "budgets": [ 51 | { 52 | "type": "initial", 53 | "maximumWarning": "2mb", 54 | "maximumError": "5mb" 55 | }, 56 | { 57 | "type": "anyComponentStyle", 58 | "maximumWarning": "6kb", 59 | "maximumError": "10kb" 60 | } 61 | ] 62 | } 63 | } 64 | }, 65 | "serve": { 66 | "builder": "@angular-devkit/build-angular:dev-server", 67 | "options": { 68 | "browserTarget": "kratos:build" 69 | }, 70 | "configurations": { 71 | "production": { 72 | "browserTarget": "kratos:build:production" 73 | } 74 | } 75 | }, 76 | "extract-i18n": { 77 | "builder": "@angular-devkit/build-angular:extract-i18n", 78 | "options": { 79 | "browserTarget": "kratos:build" 80 | } 81 | }, 82 | "test": { 83 | "builder": "@angular-devkit/build-angular:karma", 84 | "options": { 85 | "main": "src/test.ts", 86 | "polyfills": "src/polyfills.ts", 87 | "tsConfig": "tsconfig.spec.json", 88 | "karmaConfig": "karma.conf.js", 89 | "assets": [ 90 | "src/favicon.ico", 91 | "src/assets" 92 | ], 93 | "styles": [ 94 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 95 | "src/styles.css" 96 | ], 97 | "scripts": [] 98 | } 99 | }, 100 | "lint": { 101 | "builder": "@angular-devkit/build-angular:tslint", 102 | "options": { 103 | "tsConfig": [ 104 | "tsconfig.app.json", 105 | "tsconfig.spec.json", 106 | "e2e/tsconfig.json" 107 | ], 108 | "exclude": [ 109 | "**/node_modules/**" 110 | ] 111 | } 112 | }, 113 | "e2e": { 114 | "builder": "@angular-devkit/build-angular:protractor", 115 | "options": { 116 | "protractorConfig": "e2e/protractor.conf.js", 117 | "devServerTarget": "kratos:serve" 118 | }, 119 | "configurations": { 120 | "production": { 121 | "devServerTarget": "kratos:serve:production" 122 | } 123 | } 124 | } 125 | } 126 | } 127 | }, 128 | "defaultProject": "kratos" 129 | } -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('kratos app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist/kratos", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/kratos'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kratos", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~9.1.4", 15 | "@angular/cdk": "^9.2.4", 16 | "@angular/common": "~9.1.4", 17 | "@angular/compiler": "~9.1.4", 18 | "@angular/core": "~11.0.5", 19 | "@angular/fire": "^6.0.0", 20 | "@angular/forms": "~9.1.4", 21 | "@angular/localize": "~9.1.4", 22 | "@angular/material": "^9.2.4", 23 | "@angular/platform-browser": "~9.1.4", 24 | "@angular/platform-browser-dynamic": "~9.1.4", 25 | "@angular/router": "~9.1.4", 26 | "@ctrl/ngx-emoji-mart": "^4.0.2", 27 | "@ng-bootstrap/ng-bootstrap": "^6.1.0", 28 | "bootstrap": "^4.5.0", 29 | "firebase": "^7.14.6", 30 | "ngx-device-detector": "^1.4.4", 31 | "ngx-linkifyjs": "^1.3.0", 32 | "rxjs": "~6.5.4", 33 | "tslib": "^1.10.0", 34 | "zone.js": "~0.10.2" 35 | }, 36 | "devDependencies": { 37 | "@angular-devkit/architect": "~0.900", 38 | "@angular-devkit/build-angular": "~0.901.4", 39 | "@angular/cli": "~9.1.4", 40 | "@angular/compiler-cli": "~9.1.4", 41 | "@angular/language-service": "~9.1.4", 42 | "@types/jasmine": "~3.5.0", 43 | "@types/jasminewd2": "~2.0.3", 44 | "@types/node": "^12.11.1", 45 | "codelyzer": "^5.1.2", 46 | "firebase-tools": "^8.0.0", 47 | "fuzzy": "^0.1.3", 48 | "inquirer": "^6.2.2", 49 | "inquirer-autocomplete-prompt": "^1.0.1", 50 | "jasmine-core": "~3.5.0", 51 | "jasmine-spec-reporter": "~4.2.1", 52 | "karma": "~6.3.16", 53 | "karma-chrome-launcher": "~3.1.0", 54 | "karma-coverage-istanbul-reporter": "~2.1.0", 55 | "karma-jasmine": "~3.0.1", 56 | "karma-jasmine-html-reporter": "^1.4.2", 57 | "protractor": "~5.4.3", 58 | "ts-node": "~8.3.0", 59 | "tslint": "~6.1.0", 60 | "typescript": "~3.8.3" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | 5 | const routes: Routes = []; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forRoot(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class AppRoutingModule { } 12 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | #myBtn { 2 | display: none; 3 | position: fixed; 4 | bottom: 50px; 5 | right: 30px; 6 | z-index: 99; 7 | border: none; 8 | border-radius: 50%; 9 | outline: none; 10 | color: #ec9be3; 11 | background-color: #424242; 12 | cursor: pointer; 13 | padding: 1px; 14 | font-size: 18px; 15 | height: 40px; 16 | width: 40px; 17 | transition: all 0.3s; 18 | -o-transition: all 0.3s; 19 | -moz-transition: all 0.3s; 20 | -webkit-transition: all 0.3s; 21 | } 22 | 23 | #myBtn:hover { 24 | background-color: black; 25 | } 26 | 27 | .icon { 28 | margin-top: 4px; 29 | } 30 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 9 | 10 | 11 |
12 | 13 |
-------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'kratos'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.componentInstance; 26 | expect(app.title).toEqual('kratos'); 27 | }); 28 | 29 | it('should render title', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.nativeElement; 33 | expect(compiled.querySelector('.content span').textContent).toContain('kratos app is running!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { PlatformLocation } from '@angular/common'; 2 | import { Component, OnDestroy, ElementRef, ViewChild } from '@angular/core'; 3 | import { Router, NavigationStart, NavigationEnd } from '@angular/router'; 4 | import { Subscription } from 'rxjs'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.css'], 10 | }) 11 | export class AppComponent implements OnDestroy { 12 | title = 'cloakspace'; 13 | myButton; 14 | 15 | private routeScrollPositions: { [url: string]: number }[] = []; 16 | private subscriptions: Subscription[] = []; 17 | private wasPop: boolean = false; 18 | private previousNavigationUrl: string; 19 | 20 | constructor(private router: Router, private location: PlatformLocation) { 21 | window.onscroll = function () { 22 | this.myButton = document.getElementById('myBtn'); 23 | if ( 24 | document.body.scrollTop > 300 || 25 | document.documentElement.scrollTop > 300 26 | ) { 27 | this.myButton.style.display = 'block'; 28 | } else { 29 | this.myButton.style.display = 'none'; 30 | } 31 | }; 32 | 33 | history.scrollRestoration = 'manual'; 34 | 35 | location.onPopState(() => { 36 | this.wasPop = true; 37 | }); 38 | 39 | this.subscriptions.push( 40 | this.router.events.subscribe((event) => { 41 | if (event instanceof NavigationStart) { 42 | let currentUrl = 43 | this.previousNavigationUrl !== null 44 | ? this.previousNavigationUrl 45 | : event.url; 46 | this.routeScrollPositions[currentUrl] = window.pageYOffset; 47 | } else if (event instanceof NavigationEnd) { 48 | this.previousNavigationUrl = event.url; 49 | 50 | if (this.wasPop) { 51 | setTimeout(() => { 52 | window.scrollTo(0, this.routeScrollPositions[event.url] || 0); 53 | }, 300); 54 | 55 | this.wasPop = false; 56 | } else window.scrollTo(0, 0); 57 | } 58 | }) 59 | ); 60 | } 61 | 62 | ngOnDestroy(): void { 63 | this.subscriptions.forEach((subscription) => subscription.unsubscribe()); 64 | } 65 | 66 | topFunction() { 67 | document.body.scrollTop = 0; 68 | document.documentElement.scrollTop = 0; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { ClipboardModule } from '@angular/cdk/clipboard'; 2 | import { LayoutModule } from '@angular/cdk/layout'; 3 | import { NgModule } from '@angular/core'; 4 | import { AngularFireModule } from '@angular/fire'; 5 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 6 | import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; 7 | import { MatButtonModule } from '@angular/material/button'; 8 | import { MatCardModule } from '@angular/material/card'; 9 | import { MatChipsModule } from '@angular/material/chips'; 10 | import { MatDialogModule } from '@angular/material/dialog'; 11 | import { MatFormFieldModule } from '@angular/material/form-field'; 12 | import { MatIconModule } from '@angular/material/icon'; 13 | import { MatInputModule } from '@angular/material/input'; 14 | import { MatListModule } from '@angular/material/list'; 15 | import { MatMenuModule } from '@angular/material/menu'; 16 | import { MatPaginatorModule } from '@angular/material/paginator'; 17 | import { MatSelectModule } from '@angular/material/select'; 18 | import { MatSidenavModule } from '@angular/material/sidenav'; 19 | import { MatSnackBarModule } from '@angular/material/snack-bar'; 20 | import { MatSortModule } from '@angular/material/sort'; 21 | import { MatTableModule } from '@angular/material/table'; 22 | import { MatToolbarModule } from '@angular/material/toolbar'; 23 | import { BrowserModule } from '@angular/platform-browser'; 24 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 25 | import { RouterModule } from '@angular/router'; 26 | import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; 27 | import { DeviceDetectorModule } from 'ngx-device-detector'; 28 | import { NgxLinkifyjsModule } from 'ngx-linkifyjs'; 29 | 30 | import { environment } from './../environments/environment'; 31 | import { AdminLoginComponent } from './admin/admin-login/admin-login.component'; 32 | import { AdminProductsComponent } from './admin/admin-products/admin-products.component'; 33 | import { AppRoutingModule } from './app-routing.module'; 34 | import { AppComponent } from './app.component'; 35 | import { CommentsComponent } from './comments/comments.component'; 36 | import { CreatePostComponent } from './create-post/create-post.component'; 37 | import { FeedRedirectComponent } from './feed-redirect/feed-redirect.component'; 38 | import { FilterPostsComponent } from './filter-posts/filter-posts.component'; 39 | import { FooterComponent } from './footer/footer.component'; 40 | import { FullPostComponent } from './full-post/full-post.component'; 41 | import { HomeComponent } from './home/home.component'; 42 | import { AboutUsComponent } from './info/about-us/about-us.component'; 43 | import { ContactUsComponent } from './info/contact-us/contact-us.component'; 44 | import { MeetTheTeamComponent } from './info/meet-the-team/meet-the-team.component'; 45 | import { LoaderComponent } from './loader/loader.component'; 46 | import { MainNavbarComponent } from './main-navbar/main-navbar.component'; 47 | import { NotFoundComponent } from './not-found/not-found.component'; 48 | import { SearchHighlightPipe } from './pipes/search-highlight.pipe'; 49 | import { PopularComponent } from './popular/popular.component'; 50 | import { PostCardComponent } from './post-card/post-card.component'; 51 | import { PostsFilterComponent } from './posts-filter/posts-filter.component'; 52 | import { PostsComponent } from './posts/posts.component'; 53 | import { RecentComponent } from './recent/recent.component'; 54 | import { SearchComponent } from './search/search.component'; 55 | import { AdminAuthguardService } from './services/admin-authguard.service'; 56 | import { AnalyticsService } from './services/analytics.service'; 57 | import { AuthService } from './services/auth-service.service'; 58 | import { FeedbackService } from './services/feedback-service.service'; 59 | import { MailingService } from './services/mailing.service'; 60 | import { PostService } from './services/post.service'; 61 | import { TagService } from './services/tag.service'; 62 | import { SharingOptionsComponent } from './sharing-options/sharing-options.component'; 63 | import { SpinnerComponent } from './spinner/spinner.component'; 64 | import { TrendingComponent } from './trending/trending.component'; 65 | import { PrivacyPolicyComponent } from './privacy-policy/privacy-policy.component'; 66 | import { PickerModule } from '@ctrl/ngx-emoji-mart'; 67 | 68 | @NgModule({ 69 | declarations: [ 70 | AppComponent, 71 | MainNavbarComponent, 72 | AdminLoginComponent, 73 | AdminProductsComponent, 74 | HomeComponent, 75 | AboutUsComponent, 76 | MeetTheTeamComponent, 77 | ContactUsComponent, 78 | CreatePostComponent, 79 | PostCardComponent, 80 | PostsComponent, 81 | FullPostComponent, 82 | SharingOptionsComponent, 83 | SpinnerComponent, 84 | CommentsComponent, 85 | SearchComponent, 86 | FilterPostsComponent, 87 | NotFoundComponent, 88 | PostsFilterComponent, 89 | TrendingComponent, 90 | PopularComponent, 91 | LoaderComponent, 92 | FooterComponent, 93 | FeedRedirectComponent, 94 | RecentComponent, 95 | SearchHighlightPipe, 96 | PrivacyPolicyComponent, 97 | ], 98 | imports: [ 99 | BrowserModule, 100 | AppRoutingModule, 101 | BrowserAnimationsModule, 102 | DeviceDetectorModule.forRoot(), 103 | NgxLinkifyjsModule.forRoot({ 104 | enableHash: false, 105 | enableMention: false, 106 | }), 107 | LayoutModule, 108 | MatToolbarModule, 109 | MatButtonModule, 110 | MatCardModule, 111 | MatSidenavModule, 112 | MatIconModule, 113 | MatInputModule, 114 | MatDialogModule, 115 | MatSnackBarModule, 116 | MatTableModule, 117 | MatSortModule, 118 | MatPaginatorModule, 119 | MatFormFieldModule, 120 | MatSelectModule, 121 | MatMenuModule, 122 | MatListModule, 123 | MatBottomSheetModule, 124 | ClipboardModule, 125 | FormsModule, 126 | PickerModule, 127 | MatChipsModule, 128 | ReactiveFormsModule, 129 | AngularFireModule.initializeApp(environment.firebase), 130 | RouterModule.forRoot( 131 | [ 132 | { 133 | path: '', 134 | component: HomeComponent, 135 | }, 136 | 137 | { 138 | path: 'feed', 139 | component: PostsComponent, 140 | }, 141 | 142 | { 143 | path: 'post/:id', 144 | component: FullPostComponent, 145 | }, 146 | { 147 | path: 'post/**', 148 | component: NotFoundComponent, 149 | }, 150 | 151 | { 152 | path: 'search', 153 | component: SearchComponent, 154 | }, 155 | 156 | { 157 | path: 'popular', 158 | component: PopularComponent, 159 | }, 160 | { 161 | path: 'recent', 162 | component: RecentComponent, 163 | }, 164 | 165 | { 166 | path: 'about', 167 | component: AboutUsComponent, 168 | }, 169 | { 170 | path: 'team', 171 | component: MeetTheTeamComponent, 172 | }, 173 | { 174 | path: 'contact', 175 | component: ContactUsComponent, 176 | }, 177 | { 178 | path: 'policy', 179 | component: PrivacyPolicyComponent, 180 | }, 181 | 182 | { 183 | path: '9df59d4c785363a0f8148f5d5e428354', 184 | component: AdminLoginComponent, 185 | }, 186 | { 187 | path: '9df59d4c785363a0f8148f5d5e428354/products', 188 | component: AdminProductsComponent, 189 | canActivate: [AdminAuthguardService], 190 | }, 191 | 192 | { 193 | path: '404', 194 | component: NotFoundComponent, 195 | }, 196 | { 197 | path: '**', 198 | component: NotFoundComponent, 199 | }, 200 | ], 201 | {} 202 | ), 203 | NgbModule, 204 | ], 205 | providers: [ 206 | AuthService, 207 | AdminAuthguardService, 208 | TagService, 209 | PostService, 210 | FeedbackService, 211 | AnalyticsService, 212 | MailingService, 213 | ], 214 | bootstrap: [AppComponent], 215 | }) 216 | export class AppModule {} 217 | -------------------------------------------------------------------------------- /src/app/comments/comments.component.css: -------------------------------------------------------------------------------- 1 | table { 2 | width: 100%; 3 | display: block; 4 | } 5 | 6 | .mat-column-icon { 7 | max-width: 70px !important; 8 | text-align: left; 9 | } 10 | 11 | .picture { 12 | top: 0 !important; 13 | } 14 | 15 | .mat-column-icon mat-cell { 16 | text-align: left; 17 | margin-left: 0px; 18 | } 19 | 20 | mat-row { 21 | padding-top: 10px; 22 | padding-bottom: 10px; 23 | } 24 | 25 | .date { 26 | font-size: 12px; 27 | color: lightgrey; 28 | /* margin-left: 4px; */ 29 | } 30 | 31 | .text { 32 | white-space: pre-wrap; 33 | } 34 | 35 | textarea#textarea { 36 | white-space: pre-wrap; 37 | box-sizing: border-box; 38 | padding-bottom: 18px; 39 | } 40 | 41 | .example-form-field { 42 | width: 100%; 43 | margin: 15px 0px 0px 0px; 44 | } 45 | 46 | #prefixemojiicon { 47 | top: -2px; 48 | left: -2px; 49 | } 50 | 51 | @media (max-width: 700px) { 52 | #prefixemojiicon { 53 | display: none; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/comments/comments.component.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | person 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | {{ element.date | date: "MMM d, yy   H:mm" }}
21 | 22 | 23 |

24 | {{ element.replies.length }} replies  .   27 | {{ element.replies.length }} reply  .   30 | Reply 35 |   .  View all 41 |
42 |
43 |
44 | 45 | 46 | 50 |
55 | 56 | 57 | 62 | 63 | 64 | 65 | Add a reply 66 | 74 | 77 | 88 | 89 | 90 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | person_add 100 | 101 | 102 | 103 | 104 | 105 |
106 | 107 | {{ elem.date | date: "MMM d, yy   H:mm" }}
109 | 110 |
111 |
112 |
113 | 114 | 115 | 118 |
119 |
120 |
121 |
122 | 123 | 128 | 129 | 134 |
135 | -------------------------------------------------------------------------------- /src/app/comments/comments.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CommentsComponent } from './comments.component'; 4 | 5 | describe('CommentsComponent', () => { 6 | let component: CommentsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CommentsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CommentsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/comments/comments.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | animate, 3 | state, 4 | style, 5 | transition, 6 | trigger, 7 | } from '@angular/animations'; 8 | import { Component, Input } from '@angular/core'; 9 | import { ActivatedRoute } from '@angular/router'; 10 | import { Comment } from './../models/comment'; 11 | import { PostService } from './../services/post.service'; 12 | 13 | @Component({ 14 | selector: 'comments', 15 | templateUrl: './comments.component.html', 16 | styleUrls: ['./comments.component.css'], 17 | animations: [ 18 | trigger('detailExpand', [ 19 | state('collapsed', style({ height: '0px', minHeight: '0' })), 20 | state('expanded', style({ height: '*' })), 21 | transition( 22 | 'expanded <=> collapsed', 23 | animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)') 24 | ), 25 | ]), 26 | ], 27 | }) 28 | export class CommentsComponent { 29 | @Input('key') key; 30 | uid: string; 31 | displayedColumns: string[] = ['icon', 'comments']; 32 | displayedColumnsVersion: string[] = ['icon', 'comments']; 33 | dataSource: Comment[]; 34 | comments; 35 | value: string = ''; 36 | showEmojis = false; 37 | 38 | constructor(private postService: PostService, private route: ActivatedRoute) { 39 | this.route.params.subscribe((param) => { 40 | this.uid = param.id; 41 | 42 | this.postService.getAllComments(this.uid).subscribe((comments) => { 43 | this.comments = comments.reverse(); 44 | this.initializeTable(this.comments); 45 | }); 46 | }); 47 | 48 | } 49 | 50 | private initializeTable(comments: Comment[]) { 51 | this.dataSource = comments; 52 | } 53 | 54 | addReply(commentKey: string, v: string) { 55 | if (v.trim().length) { 56 | this.postService.addReplies(this.uid, commentKey, v.trim()); 57 | this.value = ''; 58 | } 59 | } 60 | 61 | addEmoji(event) { 62 | this.value += event.emoji.native; 63 | } 64 | 65 | dragemoji() { 66 | this.dragElement(document.getElementById("emojis")); 67 | } 68 | 69 | emojiSelect(event) { 70 | document.getElementById('emojis').style.top = (event.pageY - 350) + "px"; 71 | this.showEmojis = !this.showEmojis; 72 | document.getElementById('emojis').style.display = (this.showEmojis === true) ? 'block' : 'none'; 73 | 74 | this.dragElement(document.getElementById("emojis")); 75 | } 76 | 77 | dragElement(elmnt) { 78 | var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; 79 | if (document.getElementById(elmnt.id + "header")) { 80 | document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown; 81 | } else { 82 | elmnt.onmousedown = dragMouseDown; 83 | } 84 | 85 | function dragMouseDown(e) { 86 | e = e || window.event; 87 | e.preventDefault(); 88 | pos3 = e.clientX; 89 | pos4 = e.clientY; 90 | document.onmouseup = closeDragElement; 91 | document.onmousemove = elementDrag; 92 | } 93 | 94 | function elementDrag(e) { 95 | e = e || window.event; 96 | e.preventDefault(); 97 | pos1 = pos3 - e.clientX; 98 | pos2 = pos4 - e.clientY; 99 | pos3 = e.clientX; 100 | pos4 = e.clientY; 101 | elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; 102 | elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; 103 | } 104 | 105 | function closeDragElement() { 106 | document.onmouseup = null; 107 | document.onmousemove = null; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/app/create-post/create-post.component.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-weight: bold; 3 | } 4 | 5 | form { 6 | margin-bottom: 40px; 7 | } 8 | 9 | .policy { 10 | margin-top: 30px; 11 | color: darkgray; 12 | } 13 | 14 | .main-form { 15 | display: flex; 16 | justify-content: center; 17 | align-items: center; 18 | flex-direction: column; 19 | overflow: auto; 20 | } 21 | 22 | .mat-form-field { 23 | margin-bottom: 15px; 24 | } 25 | 26 | mat-form-field { 27 | width: 80% !important; 28 | } 29 | 30 | mat-error, 31 | mat-error div { 32 | font-size: 12px; 33 | } 34 | 35 | .main-body { 36 | width: 90%; 37 | white-space: pre-wrap; 38 | } 39 | 40 | .tagList { 41 | opacity: 0.75; 42 | font-size: 0.75em; 43 | } 44 | 45 | textarea { 46 | white-space: pre-wrap; 47 | } 48 | 49 | .action-buttons { 50 | display: block; 51 | margin: auto; 52 | } 53 | 54 | .post-btn { 55 | margin-right: 15px; 56 | } 57 | 58 | ::ng-deep .mat-input-wrapper { 59 | width: 100% !important; 60 | } 61 | 62 | /* ::ng-deep .mat-focused .mat-form-field-label { 63 | color: #9c27b0 !important; 64 | } */ 65 | 66 | h1 { 67 | text-align: center; 68 | font-weight: bold; 69 | margin: 0px auto 15px auto; 70 | font-family: "Quicksand", sans-serif; 71 | } 72 | 73 | .optional { 74 | margin-top: 5px; 75 | text-align: center; 76 | } 77 | 78 | #prefixemoticon, #prefixemoticon2 { 79 | top: -2px; 80 | left: -2px; 81 | } 82 | 83 | @media (max-width: 700px) { 84 | #prefixemoticon, #prefixemoticon2 { 85 | display: none; 86 | } 87 | } -------------------------------------------------------------------------------- /src/app/create-post/create-post.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 8 | 9 | 14 | 15 |

Go On! Post Something?

16 |
17 | 18 | 19 | What's on your mind? 20 | 22 | 23 |
24 | Oops. This is a required field! 25 |
26 |
27 | Title should be maximum of 60 characters. 28 |
29 |
30 | 33 |
34 | 35 | 36 | Elaborate your thoughts 37 | 40 | 41 | Come on. You gotta elaborate! 42 | 43 | 46 | 47 | 48 |
Optional to receive notifications via mail.
49 | 50 | Your Email ID 51 | 53 | 54 | 55 | Whoa, that's not a valid Email ID! 56 | 57 | 58 | 59 | 60 | Add a few tags 61 | 62 | 63 | {{tags.value ? tags.value[0] : ''}} 64 | 65 | (+{{tags.value.length - 1}} {{tags.value?.length === 2 ? 'other' : 'others'}}) 66 | 67 | 68 | 69 | {{ t.val.name }} 70 | 71 | 72 | 73 | Okay. Just this once! 74 | 75 | 76 | 77 | 78 |
79 | 82 | 83 |
84 | 85 |
Read our policy here to stay updated
86 | 87 |
88 | 89 |
-------------------------------------------------------------------------------- /src/app/create-post/create-post.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CreatePostComponent } from './create-post.component'; 4 | 5 | describe('CreatePostComponent', () => { 6 | let component: CreatePostComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CreatePostComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CreatePostComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/create-post/create-post.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { TagService } from '../services/tag.service'; 3 | import { PostService } from '../services/post.service'; 4 | import { MatDialogRef } from '@angular/material/dialog'; 5 | import { MatSnackBar } from '@angular/material/snack-bar'; 6 | import { Router, NavigationEnd } from '@angular/router'; 7 | 8 | @Component({ 9 | selector: 'app-create-post', 10 | templateUrl: './create-post.component.html', 11 | styleUrls: ['./create-post.component.css'], 12 | }) 13 | export class CreatePostComponent { 14 | tags$; 15 | post: any = {}; 16 | showEmoj = false; 17 | value = ""; 18 | value2 = ""; 19 | 20 | constructor( 21 | public _snackBar: MatSnackBar, 22 | private tagService: TagService, 23 | private postService: PostService, 24 | private dialog: MatDialogRef, 25 | private router: Router 26 | ) { 27 | this.tags$ = this.tagService.getAll(); 28 | } 29 | 30 | public get width() { 31 | return window.innerWidth; 32 | } 33 | 34 | async save(post) { 35 | const createdPost = await this.postService.createPost(post); 36 | 37 | this.dialog.close(); 38 | this.openSnackBar('Your post has been created', 'Dismiss'); 39 | this.router.navigate(['/post', createdPost.key]); 40 | } 41 | 42 | scrollTo(uid) { 43 | let element = document.getElementById(uid); 44 | element.scrollIntoView(); 45 | window.scrollTo(0, 0); 46 | } 47 | 48 | openSnackBar(message: string, action: string) { 49 | this._snackBar.open(message, action, { 50 | duration: 4000, 51 | }); 52 | } 53 | 54 | addEmoji(event) { 55 | this.value += event.emoji.native; 56 | } 57 | 58 | addEmoji2(event) { 59 | this.value2 += event.emoji.native; 60 | } 61 | 62 | dragemoji() { 63 | this.dragElement(document.getElementById("emoj")); 64 | } 65 | 66 | emojiSelect(event) { 67 | this.showEmoj = !this.showEmoj; 68 | document.getElementById('emoj').style.display = (this.showEmoj === true) ? 'block' : 'none'; 69 | 70 | this.dragElement(document.getElementById("emoj")); 71 | } 72 | 73 | emojiSelect2(event) { 74 | this.showEmoj = !this.showEmoj; 75 | document.getElementById('emojs').style.display = (this.showEmoj === true) ? 'block' : 'none'; 76 | 77 | this.dragElement(document.getElementById("emojs")); 78 | } 79 | 80 | dragElement(elmnt) { 81 | var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; 82 | if (document.getElementById(elmnt.id + "header")) { 83 | document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown; 84 | } else { 85 | elmnt.onmousedown = dragMouseDown; 86 | } 87 | 88 | function dragMouseDown(e) { 89 | e = e || window.event; 90 | e.preventDefault(); 91 | pos3 = e.clientX; 92 | pos4 = e.clientY; 93 | document.onmouseup = closeDragElement; 94 | document.onmousemove = elementDrag; 95 | } 96 | 97 | function elementDrag(e) { 98 | e = e || window.event; 99 | e.preventDefault(); 100 | pos1 = pos3 - e.clientX; 101 | pos2 = pos4 - e.clientY; 102 | pos3 = e.clientX; 103 | pos4 = e.clientY; 104 | elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; 105 | elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; 106 | } 107 | 108 | function closeDragElement() { 109 | document.onmouseup = null; 110 | document.onmousemove = null; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/app/feed-redirect/feed-redirect.component.css: -------------------------------------------------------------------------------- 1 | h3 { 2 | font-family: "Quicksand", sans-serif; 3 | font-weight: 500; 4 | } 5 | 6 | .actions-buttons { 7 | display: flex; 8 | justify-content: center; 9 | margin: 35px 0 12px; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/feed-redirect/feed-redirect.component.html: -------------------------------------------------------------------------------- 1 | 2 |

3 | You will be redirected to your main feed after this. Are you sure you want to do that? 4 |

5 |
6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /src/app/feed-redirect/feed-redirect.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FeedRedirectComponent } from './feed-redirect.component'; 4 | 5 | describe('FeedRedirectComponent', () => { 6 | let component: FeedRedirectComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FeedRedirectComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FeedRedirectComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/feed-redirect/feed-redirect.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'app-feed-redirect', 6 | templateUrl: './feed-redirect.component.html', 7 | styleUrls: ['./feed-redirect.component.css'] 8 | }) 9 | export class FeedRedirectComponent { 10 | 11 | constructor(private router: Router) { } 12 | 13 | redirectToFeed() { 14 | this.router.navigate(['/feed']); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/app/filter-posts/filter-posts.component.css: -------------------------------------------------------------------------------- 1 | .sheet-item { 2 | width: 100%; 3 | text-align: center; 4 | font-weight: bold; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/filter-posts/filter-posts.component.html: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/app/filter-posts/filter-posts.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FilterPostsComponent } from './filter-posts.component'; 4 | 5 | describe('FilterPostsComponent', () => { 6 | let component: FilterPostsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FilterPostsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FilterPostsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/filter-posts/filter-posts.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { MatBottomSheet } from '@angular/material/bottom-sheet'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { TagService } from '../services/tag.service'; 6 | import { Router } from '@angular/router'; 7 | 8 | @Component({ 9 | selector: 'filter-posts', 10 | templateUrl: './filter-posts.component.html', 11 | styleUrls: ['./filter-posts.component.css'] 12 | }) 13 | export class FilterPostsComponent implements OnInit { 14 | 15 | tags$: Observable; 16 | 17 | constructor( 18 | private tagService: TagService, 19 | private bottomSheet: MatBottomSheet, 20 | private router: Router 21 | ) { } 22 | 23 | async ngOnInit() { 24 | this.tags$ = await this.tagService.getAll(); 25 | } 26 | 27 | applyFilter(tag: string) { 28 | 29 | this.bottomSheet.dismiss(); 30 | 31 | this.router.navigate(['/search'], { 32 | queryParams: { 33 | tag: tag 34 | }, 35 | queryParamsHandling: 'merge' 36 | }); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/app/footer/footer.component.css: -------------------------------------------------------------------------------- 1 | footer { 2 | margin-top: 60px; 3 | } 4 | 5 | .footer-main { 6 | background-color: #292c2f; 7 | box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.12); 8 | box-sizing: border-box; 9 | width: 100%; 10 | text-align: center; 11 | 12 | padding: 20px 50px; 13 | /* margin-top: -40px; */ 14 | } 15 | 16 | .footer-main .footer-stats { 17 | display: flex; 18 | flex-direction: row; 19 | justify-content: center; 20 | align-items: center; 21 | } 22 | 23 | .footer-main .footer-stats span { 24 | margin: 0 18px; 25 | } 26 | 27 | .footer-main .footer-links { 28 | color: #ffffff; 29 | margin: 20px 0 20px; 30 | padding: 0; 31 | } 32 | 33 | .footer-main .footer-links a { 34 | display: inline-block; 35 | line-height: 1.8; 36 | text-decoration: none; 37 | color: inherit; 38 | cursor: pointer; 39 | margin: 0 12px; 40 | } 41 | 42 | .footer-main .footer-company-name { 43 | color: #8f9296; 44 | font-size: 14px; 45 | font-weight: normal; 46 | margin: 0; 47 | } 48 | 49 | @media (max-width: 992px) { 50 | .footer-main { 51 | font: bold 14px; 52 | } 53 | } 54 | 55 | @media (max-width: 770px) { 56 | .footer-main .footer-stats { 57 | flex-direction: column; 58 | } 59 | 60 | .footer-main .footer-stats span { 61 | margin: 5px 0; 62 | } 63 | 64 | .footer-main .footer-links { 65 | display: flex; 66 | flex-direction: column; 67 | margin: 35px 0; 68 | } 69 | 70 | .footer-main .footer-company-name { 71 | display: flex; 72 | flex-direction: column; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/app/footer/footer.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 23 | 24 | 25 | 38 | 39 | 43 | 44 |
-------------------------------------------------------------------------------- /src/app/footer/footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FooterComponent } from './footer.component'; 4 | 5 | describe('FooterComponent', () => { 6 | let component: FooterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FooterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FooterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { AnalyticsService } from '../services/analytics.service'; 4 | import { PostService } from '../services/post.service'; 5 | import { combineLatest, Subscription } from 'rxjs'; 6 | import { take } from 'rxjs/operators'; 7 | 8 | @Component({ 9 | selector: 'app-footer', 10 | templateUrl: './footer.component.html', 11 | styleUrls: ['./footer.component.css'] 12 | }) 13 | export class FooterComponent { 14 | 15 | date: number = new Date().getFullYear(); 16 | pageVisits = 0; 17 | totalLikes = 0; 18 | totalComments = 0; 19 | totalPosts = 0; 20 | isLoading: boolean = true; 21 | 22 | constructor( 23 | private router: Router, 24 | private analyticsService: AnalyticsService, 25 | private postService: PostService) { 26 | 27 | combineLatest( 28 | this.analyticsService.getCurrentPageVisits(), 29 | this.postService.getAllPosts() 30 | ).pipe(take(1)).subscribe(combined => { 31 | 32 | this.pageVisits = combined[0][0]; 33 | this.totalPosts = combined[1].length; 34 | combined[1].forEach(post => { 35 | this.totalLikes += post.likes; 36 | this.totalComments += (post.comments) ? Object.keys(post.comments).length : 0; 37 | }); 38 | 39 | this.isLoading = false; 40 | }); 41 | } 42 | 43 | navigate(action: string) { 44 | switch (action) { 45 | case 'home': 46 | this.router.navigate(['/']); 47 | break; 48 | 49 | case 'feed': 50 | this.router.navigate(['/feed']); 51 | break; 52 | 53 | case 'about': 54 | this.router.navigate(['/about']); 55 | break; 56 | 57 | case 'policy': 58 | this.router.navigate(['/policy']); 59 | break; 60 | 61 | case 'team': 62 | this.router.navigate(['/team']); 63 | break; 64 | 65 | case 'contact': 66 | this.router.navigate(['/contact']); 67 | break; 68 | 69 | default: 70 | break; 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/app/full-post/full-post.component.css: -------------------------------------------------------------------------------- 1 | .example-form-field { 2 | margin-bottom: 10px; 3 | width: 100%; 4 | } 5 | 6 | mat-card { 7 | margin-bottom: 10px !important; 8 | } 9 | 10 | .alert { 11 | font-size: 16px; 12 | text-align: center; 13 | border: 1px solid grey; 14 | user-select: none; 15 | } 16 | 17 | .row { 18 | margin-bottom: 20px; 19 | } 20 | 21 | ::ng-deep .mat-focused .mat-form-field-label { 22 | color: #ffc824 !important; 23 | } 24 | 25 | .post-comment { 26 | padding-top: 2px; 27 | margin-bottom: 10px; 28 | } 29 | 30 | textarea#textarea { 31 | white-space: pre-wrap; 32 | box-sizing: border-box; 33 | padding-bottom: 22px; 34 | } 35 | 36 | @media (max-width: 992px) { 37 | .col-sm-0 { 38 | display: none; 39 | } 40 | } 41 | 42 | #prefixemojiicon { 43 | top: -7px; 44 | left: -2px; 45 | } 46 | 47 | @media (max-width: 700px) { 48 | #prefixemojiicon { 49 | display: none; 50 | } 51 | } -------------------------------------------------------------------------------- /src/app/full-post/full-post.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 | 7 |
8 |
9 |
10 |
11 | 12 | 13 | 18 | 19 | 20 | Speak your heart out 21 | 29 | 32 | 43 | 44 | 45 | 46 | 47 |
48 | No comments yet! 49 |
50 |
51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 | -------------------------------------------------------------------------------- /src/app/full-post/full-post.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FullPostComponent } from './full-post.component'; 4 | 5 | describe('FullPostComponent', () => { 6 | let component: FullPostComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FullPostComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FullPostComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/full-post/full-post.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ActivatedRoute, Router } from '@angular/router'; 3 | 4 | import { Post } from '../models/post'; 5 | import { PostService } from '../services/post.service'; 6 | 7 | @Component({ 8 | selector: 'app-full-post', 9 | templateUrl: './full-post.component.html', 10 | styleUrls: ['./full-post.component.css'], 11 | }) 12 | export class FullPostComponent { 13 | post: Post; 14 | isLoading: boolean = true; 15 | value: string = ''; 16 | showEmoji = false; 17 | 18 | constructor( 19 | private postService: PostService, 20 | private route: ActivatedRoute, 21 | private router: Router 22 | ) { 23 | this.route.params.subscribe((param) => { 24 | const uid = param.id; 25 | 26 | this.postService.getPostById(uid).subscribe((post) => { 27 | if (!post || !Object.keys(post).length) this.router.navigate(['/post']); 28 | 29 | this.post = post; 30 | this.isLoading = false; 31 | }); 32 | }); 33 | } 34 | 35 | postComment(v: string) { 36 | if (v.trim().length) { 37 | this.postService.addComment(this.post.key, v.trim()); 38 | this.value = ''; 39 | } 40 | } 41 | 42 | addEmoji(event) { 43 | this.value += event.emoji.native; 44 | } 45 | 46 | dragemoji() { 47 | this.dragElement(document.getElementById("emoji")); 48 | } 49 | 50 | emojiSelect(event) { 51 | document.getElementById('emoji').style.top = (event.pageY - 350) + "px"; 52 | this.showEmoji = !this.showEmoji; 53 | document.getElementById('emoji').style.display = (this.showEmoji === true) ? 'block' : 'none'; 54 | 55 | this.dragElement(document.getElementById("emoji")); 56 | } 57 | 58 | dragElement(elmnt) { 59 | var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; 60 | if (document.getElementById(elmnt.id + "header")) { 61 | document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown; 62 | 63 | } else { 64 | elmnt.onmousedown = dragMouseDown; 65 | } 66 | 67 | function dragMouseDown(e) { 68 | e = e || window.event; 69 | e.preventDefault(); 70 | pos3 = e.clientX; 71 | pos4 = e.clientY; 72 | document.onmouseup = closeDragElement; 73 | document.onmousemove = elementDrag; 74 | } 75 | 76 | function elementDrag(e) { 77 | e = e || window.event; 78 | e.preventDefault(); 79 | pos1 = pos3 - e.clientX; 80 | pos2 = pos4 - e.clientY; 81 | pos3 = e.clientX; 82 | pos4 = e.clientY; 83 | elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; 84 | elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; 85 | } 86 | 87 | function closeDragElement() { 88 | document.onmouseup = null; 89 | document.onmousemove = null; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/app/home/home.component.css: -------------------------------------------------------------------------------- 1 | .main-wrapper { 2 | padding: 20px 0; 3 | } 4 | 5 | .masthead-brand { 6 | font-size: 2.5rem; 7 | font-family: "Quicksand", sans-serif; 8 | } 9 | 10 | .inner { 11 | padding: 10px 0; 12 | margin: 20px auto; 13 | } 14 | 15 | .logo-body { 16 | width: 110px; 17 | height: 110px; 18 | border-radius: 50%; 19 | } 20 | 21 | .cover-heading { 22 | margin: 10px 0; 23 | font-family: "Quicksand", sans-serif; 24 | font-size: 1.5rem; 25 | } 26 | 27 | .main-btn { 28 | width: 12rem; 29 | height: 3rem; 30 | margin-top: 35px; 31 | margin-bottom: 20px; 32 | } 33 | 34 | h2 { 35 | font-family: "Quicksand", sans-serif; 36 | font-size: 30px; 37 | margin-top: 15px; 38 | } 39 | 40 | .image { 41 | width: 80px; 42 | height: 80px; 43 | margin-top: 20px; 44 | } 45 | 46 | .bind { 47 | border-radius: 50%; 48 | width: 120px; 49 | height: 120px; 50 | margin: 0 auto; 51 | margin-bottom: 20px; 52 | } 53 | 54 | .col-md-4 { 55 | margin-bottom: 50px; 56 | max-width: 320px; 57 | padding: 15px; 58 | margin: 0 auto; 59 | } 60 | 61 | /* .bind { 62 | max-width: 320px; 63 | margin: 0px auto; 64 | padding: 15px; 65 | background-color: #424242; 66 | -webkit-box-shadow: 3px 3px 11px 0px rgba(0, 0, 0, 0.88); 67 | -moz-box-shadow: 3px 3px 11px 0px rgba(0, 0, 0, 0.88); 68 | box-shadow: 3px 3px 11px 0px rgba(0, 0, 0, 0.88); 69 | } */ 70 | 71 | .sec-btn { 72 | margin-top: 35px; 73 | } 74 | 75 | @media (min-width: 992px) { 76 | .image { 77 | width: 110px; 78 | height: 110px; 79 | } 80 | 81 | .bind { 82 | width: 150px; 83 | height: 150px; 84 | } 85 | } 86 | 87 | @media (min-width: 641px) { 88 | .sec-btn { 89 | display: none; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | logo 6 |

Cloakspace

7 |
8 |
9 | 10 | 11 |
12 |

A platform where you don't trade anonymity for your thoughts.

13 |

14 | 17 |

18 |
19 |
20 | 21 |
22 |
23 |
24 | 25 |
26 |

Trending

27 |

Welcome to the new order. Contribute your thoughts without fear.

28 |
29 |
30 |
31 | 32 |
33 |

Secure

34 |

Don't compromise on data security. We do not save any of your data.

35 |
36 |
37 |
38 | 39 |
40 |

Cool

41 |

It's just as any platform with features but without the tracking.

42 |
43 |
44 | 45 | 48 |
-------------------------------------------------------------------------------- /src/app/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | describe('HomeComponent', () => { 6 | let component: HomeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HomeComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HomeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-home', 5 | templateUrl: './home.component.html', 6 | styleUrls: ['./home.component.css'] 7 | }) 8 | export class HomeComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/info/about-us/about-us.component.css: -------------------------------------------------------------------------------- 1 | .text { 2 | text-align: left; 3 | /* text-justify: newspaper; */ 4 | } 5 | 6 | .inner { 7 | display: flex; 8 | justify-content: center; 9 | margin-bottom: 45px; 10 | margin-top: -10px; 11 | } 12 | 13 | .logo-body { 14 | width: 178px; 15 | height: 178px; 16 | border-radius: 50%; 17 | } 18 | 19 | .credits { 20 | color: rgb(165, 165, 165); 21 | margin-bottom: 20px; 22 | } 23 | 24 | .row { 25 | margin-top: 30px; 26 | margin-bottom: 60px; 27 | } 28 | 29 | .row button { 30 | font-weight: bold; 31 | } 32 | 33 | h1 { 34 | text-align: center; 35 | font-family: "Quicksand", sans-serif; 36 | } 37 | 38 | .container2 { 39 | margin-top: 60px; 40 | } 41 | 42 | .verbosy { 43 | margin-bottom: 60px; 44 | } 45 | -------------------------------------------------------------------------------- /src/app/info/about-us/about-us.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | logo 5 |
6 | 7 |

FLUXTON INC.

8 |
9 | Fluxton Incorporation is a completely FICTIONAL company run by the two of us. It basically serves as a front 10 | from behind which we work on various projects that catch our attention. It helps us strike a balance between 11 | professionalism and keeping things simple. This is however, neither a business venture nor an organization 12 | striving on profits. Our projects and ideas are completely driven by our passion towards technology and software 13 | and we work on these to further strengthen our grasp on various languages and technology. In one line, Fluxton 14 | serves as a virtual wall behind which two aspiring developers work hard to bring a change. 15 |

16 | If you have any queries or suggestions, please feel free to contact us and we would be more than happy to have a 17 | chat with you. If you are not quite sure of how we are pulling this website off while maintaining anonymity, get 18 | in touch with us so that we can help you understand the process. 19 |
20 | 21 |
22 |
24 |
26 |
27 | 28 |

ABOUT CLOAKSPACE

29 |
30 | Cloakspace is not your typical social media platform which logs all sorts of data about the user. We do not 31 | store 32 | any of your data and simultaneously provide a smooth experience. We tried to provide all sorts of features which 33 | can be exploited without storing information about the user. There are no hidden costs involved. This is a 34 | completely free platform where you can leave your thoughts and read some more without constantly worrying of 35 | being tracked or judged by someone. 36 |

37 | We hope that you have an enriching experience as you scroll through the feeds. Do leave a suggestion or feedback 38 | so that we can improve and cater to your needs. 39 |

40 | NOTE: Cloakspace is best viewed on devices having a screen width of more than 1000px, that is laptops and 41 | desktops. However, other devices pretty much provide a great experience too. 42 |
43 | 44 |
45 | CREDITS: 46 |

47 | Logo vector created by 48 | freepik - www.freepik.com 49 |
50 |
Icons made by 51 | Freepik from www.flaticon.com 53 |
54 |
55 |
-------------------------------------------------------------------------------- /src/app/info/about-us/about-us.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AboutUsComponent } from './about-us.component'; 4 | 5 | describe('AboutUsComponent', () => { 6 | let component: AboutUsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ AboutUsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AboutUsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/info/about-us/about-us.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-about-us', 5 | templateUrl: './about-us.component.html', 6 | styleUrls: ['./about-us.component.css'] 7 | }) 8 | export class AboutUsComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/info/contact-us/contact-us.component.css: -------------------------------------------------------------------------------- 1 | form { 2 | font-weight: bold; 3 | } 4 | 5 | form { 6 | margin-bottom: 40px; 7 | } 8 | 9 | .main-form { 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | flex-direction: column; 14 | overflow: auto; 15 | margin-bottom: 50px; 16 | } 17 | 18 | .mat-form-field { 19 | margin-bottom: 15px; 20 | } 21 | 22 | mat-form-field { 23 | width: 80% !important; 24 | } 25 | 26 | .mat-error { 27 | font-size: 12px; 28 | } 29 | 30 | .main-body { 31 | width: 90%; 32 | white-space: pre-wrap; 33 | } 34 | 35 | .tagList { 36 | opacity: 0.75; 37 | font-size: 0.75em; 38 | } 39 | 40 | textarea { 41 | white-space: pre-wrap; 42 | } 43 | 44 | .action-buttons { 45 | display: block; 46 | margin: auto; 47 | } 48 | 49 | .post-btn { 50 | margin-right: 15px; 51 | font-weight: bold; 52 | } 53 | 54 | ::ng-deep .mat-input-wrapper { 55 | width: 100% !important; 56 | } 57 | 58 | h1 { 59 | text-align: center; 60 | font-weight: 0; 61 | margin: 50px auto 30px auto; 62 | font-family: "Quicksand", sans-serif; 63 | } 64 | 65 | h2 { 66 | text-align: center; 67 | font-size: 30px; 68 | font-weight: 0; 69 | margin: 20px auto 30px auto; 70 | font-family: "Quicksand", sans-serif; 71 | } 72 | 73 | .optional { 74 | margin-top: 5px; 75 | text-align: center; 76 | } 77 | 78 | .container2 { 79 | margin-top: 60px; 80 | } 81 | 82 | ::ng-deep .mat-focused .mat-form-field-label { 83 | color: #ffc824 !important; 84 | } 85 | 86 | /* .text { 87 | text-align: justify; 88 | text-justify: newspaper; 89 | } */ 90 | -------------------------------------------------------------------------------- /src/app/info/contact-us/contact-us.component.html: -------------------------------------------------------------------------------- 1 |
2 |

CONTACT US

3 |
4 | We hope you are finding this website smooth enough for your use. If you face any issues or want to report any 5 | grievances, please leave a feedback below. If you want to learn more about this website or how it works, leave a 6 | feedback below. 7 |

8 | NOTE: If you wish to hear back from us, please add your email while filling up the form. 9 |
10 |

LEAVE A FEEDBACK

11 |
12 | 13 | 14 | What's this about? 15 | 17 | 18 |
19 | Topic should be maximum of 60 characters. 20 |
21 |
22 |
23 | 24 | 25 | Elaborate your thoughts 26 | 28 | 29 | Oops! This is a required field. 30 | 31 | 32 | 33 |
Totally optional, but just in case we need to contact you.
34 | 35 | Your Email ID 36 | 39 | 40 | 41 | Whoa, that's not a valid Email ID! 42 | 43 | 44 | 45 |
46 | 49 |
50 | 51 |
52 | 53 |
-------------------------------------------------------------------------------- /src/app/info/contact-us/contact-us.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ContactUsComponent } from './contact-us.component'; 4 | 5 | describe('ContactUsComponent', () => { 6 | let component: ContactUsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ContactUsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ContactUsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/info/contact-us/contact-us.component.ts: -------------------------------------------------------------------------------- 1 | import { FeedbackService } from './../../services/feedback-service.service'; 2 | import { Feedback } from './../../models/feedback'; 3 | import { Component, OnInit, ViewChild } from '@angular/core'; 4 | import { MatSnackBar } from '@angular/material/snack-bar'; 5 | 6 | @Component({ 7 | selector: 'app-contact-us', 8 | templateUrl: './contact-us.component.html', 9 | styleUrls: ['./contact-us.component.css'] 10 | }) 11 | export class ContactUsComponent implements OnInit { 12 | @ViewChild('f') myForm; 13 | 14 | constructor(private feedbackService: FeedbackService, 15 | public _snackBar: MatSnackBar) { } 16 | 17 | ngOnInit(): void { 18 | } 19 | 20 | scrollTo(input: HTMLInputElement) { 21 | input.scrollIntoView(); 22 | } 23 | 24 | save(feed) { 25 | let x = { 26 | title: feed.title, 27 | body: feed.body, 28 | email: (feed.email) ? feed.email : 'not provided', 29 | }; 30 | 31 | this.feedbackService.createFeedback(x); 32 | this._snackBar.open("Your feedback has been submitted", "Okay", { 33 | duration: 6000, 34 | }); 35 | 36 | this.myForm.resetForm(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/info/meet-the-team/meet-the-team.component.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-family: "Quicksand", sans-serif; 3 | text-align: center; 4 | margin-bottom: 20px; 5 | } 6 | 7 | .container2 { 8 | margin-top: 80px; 9 | } 10 | 11 | .picsubham, 12 | .picsubhranil { 13 | height: 100px; 14 | width: 100px; 15 | object-fit: contain; 16 | border-radius: 50%; 17 | margin-bottom: 10px; 18 | } 19 | 20 | .name { 21 | color: lightgrey; 22 | } 23 | 24 | .text { 25 | margin-bottom: 40px; 26 | } 27 | 28 | .btn-group { 29 | height: 40px; 30 | margin-top: 35px; 31 | } 32 | 33 | .btn-group img { 34 | height: 30px; 35 | width: 30px; 36 | border-radius: 50%; 37 | object-fit: contain; 38 | margin-right: 15px; 39 | cursor: pointer; 40 | transition: all 0.3s; 41 | -webkit-transition: all 0.3s; 42 | -moz-transition: all 0.3s; 43 | -o-transition: all 0.3s; 44 | } 45 | 46 | .btn-group img:hover { 47 | height: 35px; 48 | width: 35px; 49 | margin-right: 10px; 50 | } 51 | 52 | #facebook, 53 | #linkedin, 54 | #gmail { 55 | height: 35px; 56 | width: 35px; 57 | margin-top: -3px; 58 | transition: all 0.3s; 59 | -webkit-transition: all 0.3s; 60 | -moz-transition: all 0.3s; 61 | -o-transition: all 0.3s; 62 | } 63 | 64 | #facebook:hover, 65 | #linkedin:hover, 66 | #gmail:hover { 67 | height: 40px; 68 | width: 40px; 69 | margin-right: 10px; 70 | } 71 | 72 | .contact { 73 | margin: 10px 0px 20px 0px; 74 | } 75 | 76 | .col-sm-6 { 77 | margin-bottom: 40px; 78 | } 79 | 80 | /* .text { 81 | text-align: justify; 82 | text-justify: newspaper; 83 | } */ 84 | 85 | .row { 86 | margin-bottom: 25px; 87 | } 88 | -------------------------------------------------------------------------------- /src/app/info/meet-the-team/meet-the-team.component.html: -------------------------------------------------------------------------------- 1 |
2 |

MEET THE CREATORS

3 |
4 | We are two creatures who have a passion for software technologies. As we aspire to become professional developers 5 | one day, we keep working on different projects together. This along with the frequent discussions enhances our 6 | skillset. The partnership is also fuelled by our lifelong friendship which is ultimately the reason behind opening 7 | a fictitious company for our own benefits. 8 |

9 | If you have any queries or suggestions, please feel free to contact one of us and we would be more than happy to 10 | have a chat with you. If you are not quite sure of how we are pulling this website off while maintaining 11 | anonymity, get in touch with us so that we can help you understand the process. 12 |

13 |
14 | 15 |
16 |
17 | Subhranil Dutta 18 |
19 |
20 | Subhranil Dutta 21 |
22 | subhronil.dutta@gmail.com 23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 | 32 |
33 |
34 |
35 | Subham Das 36 |
37 |
38 | Subham Das 39 |
40 | das.jishu25@gmail.com 41 |
42 | 43 |
44 | 45 | 46 | 47 | 48 | 50 |
51 |
52 |
53 |
-------------------------------------------------------------------------------- /src/app/info/meet-the-team/meet-the-team.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { MeetTheTeamComponent } from './meet-the-team.component'; 4 | 5 | describe('MeetTheTeamComponent', () => { 6 | let component: MeetTheTeamComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ MeetTheTeamComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(MeetTheTeamComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/info/meet-the-team/meet-the-team.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-meet-the-team', 5 | templateUrl: './meet-the-team.component.html', 6 | styleUrls: ['./meet-the-team.component.css'] 7 | }) 8 | export class MeetTheTeamComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | contacttubu(str) { 16 | let link = ""; 17 | switch(str) { 18 | case 'twitter': 19 | link = "https://twitter.com/sdotdutta?s=09"; 20 | break; 21 | case 'github': 22 | link = "https://github.com/alpha037"; 23 | break; 24 | case 'facebook': 25 | link = "https://www.facebook.com/alpha.037"; 26 | break; 27 | case 'linkedin': 28 | link = "https://www.linkedin.com/in/shubhranil-dutta-profile"; 29 | break; 30 | default: 31 | link = "mailto:subhronil.dutta@gmail.com"; 32 | break; 33 | } 34 | window.open(link, '_blank'); 35 | } 36 | 37 | contactnaru(str) { 38 | let link = ""; 39 | switch(str) { 40 | case 'twitter': 41 | link = "https://twitter.com/lord_danton"; 42 | break; 43 | case 'github': 44 | link = "https://github.com/das-jishu"; 45 | break; 46 | case 'facebook': 47 | link = "https://www.facebook.com/subham.das.39948"; 48 | break; 49 | case 'linkedin': 50 | link = "https://www.linkedin.com/in/subham-das-profile"; 51 | break; 52 | default: 53 | link = "mailto:das.jishu25@gmail.com"; 54 | break; 55 | } 56 | window.open(link, '_blank'); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/app/loader/loader.component.css: -------------------------------------------------------------------------------- 1 | .spinner { 2 | margin: 0px auto 0; 3 | width: 70px; 4 | text-align: center; 5 | } 6 | 7 | .spinner > div { 8 | width: 18px; 9 | height: 18px; 10 | /* background-color: #fff; */ 11 | background-color: #eea7e6; 12 | 13 | border-radius: 100%; 14 | display: inline-block; 15 | -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both; 16 | animation: sk-bouncedelay 1.4s infinite ease-in-out both; 17 | } 18 | 19 | .spinner .bounce1 { 20 | -webkit-animation-delay: -0.32s; 21 | animation-delay: -0.32s; 22 | } 23 | 24 | .spinner .bounce2 { 25 | -webkit-animation-delay: -0.16s; 26 | animation-delay: -0.16s; 27 | } 28 | 29 | @-webkit-keyframes sk-bouncedelay { 30 | 0%, 31 | 80%, 32 | 100% { 33 | -webkit-transform: scale(0); 34 | } 35 | 40% { 36 | -webkit-transform: scale(1); 37 | } 38 | } 39 | 40 | @keyframes sk-bouncedelay { 41 | 0%, 42 | 80%, 43 | 100% { 44 | -webkit-transform: scale(0); 45 | transform: scale(0); 46 | } 47 | 40% { 48 | -webkit-transform: scale(1); 49 | transform: scale(1); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/app/loader/loader.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
-------------------------------------------------------------------------------- /src/app/loader/loader.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoaderComponent } from './loader.component'; 4 | 5 | describe('LoaderComponent', () => { 6 | let component: LoaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ LoaderComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LoaderComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/loader/loader.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'loader', 5 | templateUrl: './loader.component.html', 6 | styleUrls: ['./loader.component.css'] 7 | }) 8 | export class LoaderComponent { 9 | 10 | constructor() { } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/app/main-navbar/main-navbar.component.css: -------------------------------------------------------------------------------- 1 | .sidenav-container { 2 | height: 100%; 3 | } 4 | 5 | .sidenav { 6 | width: 200px; 7 | } 8 | 9 | .sidenav .mat-toolbar { 10 | background: inherit; 11 | } 12 | 13 | .mat-toolbar { 14 | height: 60px; 15 | } 16 | 17 | .mat-toolbar.mat-primary { 18 | position: fixed; 19 | top: 0; 20 | z-index: 10; 21 | } 22 | 23 | .fill-remaining-space { 24 | flex: 1 1 auto; 25 | } 26 | 27 | .fix-bottom { 28 | position: absolute; 29 | bottom: 0; 30 | left: 20px; 31 | margin-bottom: 15px; 32 | color: darkgray; 33 | user-select: none; 34 | font-weight: bold; 35 | opacity: 0.8; 36 | } 37 | 38 | .create-post-button { 39 | margin-right: 0.8rem; 40 | } 41 | 42 | .search-icon { 43 | margin-right: 0.8rem; 44 | } 45 | 46 | .filter-icon { 47 | margin-right: 0.8rem; 48 | } 49 | 50 | .mat-icon-explore { 51 | margin-bottom: 5px; 52 | margin-top: 2px; 53 | } 54 | 55 | .mat-icon-sidemenu { 56 | margin-top: -4px; 57 | } 58 | 59 | .mat-icon-waves { 60 | margin-top: 2px; 61 | margin-bottom: 3px; 62 | } 63 | 64 | .mat-icon-create { 65 | margin-top: 2px; 66 | margin-bottom: 5px; 67 | margin-right: 3px; 68 | } 69 | 70 | .mat-icon-search { 71 | margin-top: 2px; 72 | margin-bottom: 5px; 73 | } 74 | 75 | .mat-icon-filter { 76 | margin-top: 2px; 77 | margin-bottom: 5px; 78 | } 79 | 80 | .verbosy { 81 | margin-left: 8px; 82 | font-size: 16px; 83 | } 84 | 85 | .logo-main { 86 | text-decoration: none; 87 | color: white; 88 | } 89 | 90 | .logo-main img { 91 | width: 30px; 92 | height: 30px; 93 | border-radius: 50%; 94 | margin-top: -5px; 95 | } 96 | 97 | .show { 98 | display: block; 99 | max-width: 350px; 100 | transform: scale(1); 101 | margin-right: 20px; 102 | } 103 | 104 | .hide { 105 | display: none; 106 | max-width: 0px; 107 | transform: scale(0); 108 | } 109 | 110 | .search-bar { 111 | flex: 1; 112 | z-index: 999; 113 | font-size: 14px; 114 | width: 350px; 115 | height: 35px; 116 | border: none; 117 | outline: none; 118 | border-radius: 16px; 119 | padding: 8px 12px; 120 | margin-right: 10px; 121 | display: block; 122 | transition: all 0.3s ease; 123 | } 124 | 125 | ::ng-deep .tag-menu-content button { 126 | font-family: "Quicksand", sans-serif; 127 | } 128 | 129 | ::ng-deep .tag-menu-content { 130 | height: 50vh !important; 131 | } 132 | 133 | @media only screen and (max-width: 600px) { 134 | .verbosy { 135 | /* margin-left: 4px; 136 | font-size: 13px; */ 137 | display: none; 138 | } 139 | 140 | .mat-icon-waves { 141 | margin-left: -12px; 142 | margin-top: 8px; 143 | font-size: 18px; 144 | } 145 | 146 | .mat-icon-sidemenu { 147 | margin-top: -4px; 148 | } 149 | } 150 | 151 | @media (min-width: 992px) { 152 | .filter-icon, 153 | .small-search, 154 | .small-create { 155 | display: none; 156 | } 157 | } 158 | 159 | @media (max-width: 992px) { 160 | .btn-search, 161 | .btn-create { 162 | display: none; 163 | } 164 | 165 | .small-search, 166 | .small-create { 167 | display: block; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/app/main-navbar/main-navbar.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 11 | Fluxton Inc. 14 | 15 | Home 16 | Feed 17 | About Us 20 | Policy 23 | Meet The Team 26 | Contact Us 28 | 29 | 30 | 31 | v1.5.3 32 | 33 | 34 | 35 | 36 | 37 | 47 | 48 | 54 | logo 55 | 56 | Cloakspace 57 | 58 | 59 | 60 | 61 | 64 | 75 | 76 | 86 | 87 | 88 | 95 | 96 | 97 | 106 | 107 | 116 | 117 | 127 | 128 | 138 | 139 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 |
152 | -------------------------------------------------------------------------------- /src/app/main-navbar/main-navbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { LayoutModule } from '@angular/cdk/layout'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { NoopAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatListModule } from '@angular/material/list'; 7 | import { MatSidenavModule } from '@angular/material/sidenav'; 8 | import { MatToolbarModule } from '@angular/material/toolbar'; 9 | 10 | import { MainNavbarComponent } from './main-navbar.component'; 11 | 12 | describe('MainNavbarComponent', () => { 13 | let component: MainNavbarComponent; 14 | let fixture: ComponentFixture; 15 | 16 | beforeEach(async(() => { 17 | TestBed.configureTestingModule({ 18 | declarations: [MainNavbarComponent], 19 | imports: [ 20 | NoopAnimationsModule, 21 | LayoutModule, 22 | MatButtonModule, 23 | MatIconModule, 24 | MatListModule, 25 | MatSidenavModule, 26 | MatToolbarModule, 27 | ] 28 | }).compileComponents(); 29 | })); 30 | 31 | beforeEach(() => { 32 | fixture = TestBed.createComponent(MainNavbarComponent); 33 | component = fixture.componentInstance; 34 | fixture.detectChanges(); 35 | }); 36 | 37 | it('should compile', () => { 38 | expect(component).toBeTruthy(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/app/main-navbar/main-navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, OnDestroy } from '@angular/core'; 2 | import { MatDialog } from '@angular/material/dialog'; 3 | import { NavigationEnd, Router } from '@angular/router'; 4 | 5 | import { CreatePostComponent } from '../create-post/create-post.component'; 6 | import { TagService } from '../services/tag.service'; 7 | import { Subscription } from 'rxjs'; 8 | 9 | @Component({ 10 | selector: 'main-navbar', 11 | templateUrl: './main-navbar.component.html', 12 | styleUrls: ['./main-navbar.component.css'], 13 | }) 14 | export class MainNavbarComponent implements OnInit, OnDestroy { 15 | toggleDark: boolean = false; 16 | 17 | toggleSearch: boolean = false; 18 | smallScreenToggle: boolean = false; 19 | searchValue: string; 20 | 21 | hideIconsToggle: boolean; 22 | showFooter: boolean; 23 | 24 | tags: any[]; 25 | tagSubscription: Subscription; 26 | 27 | constructor( 28 | private dialog: MatDialog, 29 | private router: Router, 30 | private tagService: TagService) { 31 | 32 | this.router.events.subscribe(event => { 33 | 34 | if (event instanceof NavigationEnd) { 35 | const url = event.url; 36 | 37 | if (!url.includes('search')) 38 | this.onBackArrow(); 39 | 40 | if (url === '/' || url === '/about' || url === '/policy' || url === '/contact' || url === '/team') { 41 | this.showFooter = true; 42 | this.hideIconsToggle = true; 43 | } 44 | else { 45 | this.showFooter = false; 46 | this.hideIconsToggle = false; 47 | } 48 | } 49 | }); 50 | } 51 | 52 | ngOnInit(): void { 53 | this.tagSubscription = this.tagService.getAll() 54 | .subscribe(tags => this.tags = tags); 55 | } 56 | 57 | ngOnDestroy(): void { 58 | this.tagSubscription.unsubscribe(); 59 | } 60 | 61 | 62 | openDialog() { 63 | this.dialog.open(CreatePostComponent, { 64 | autoFocus: false, 65 | width: '100%', 66 | height: '100%', 67 | maxWidth: '100vw', 68 | maxHeight: '110vh', 69 | }); 70 | } 71 | 72 | onToggleSearch() { 73 | this.toggleSearch = !this.toggleSearch; 74 | 75 | if (window.innerWidth < 768) 76 | this.smallScreenToggle = true; 77 | } 78 | 79 | onBackArrow() { 80 | this.toggleSearch = false; 81 | this.smallScreenToggle = false; 82 | this.searchValue = ''; 83 | } 84 | 85 | searchPost(value: string) { 86 | if (!value || value === '.') return; 87 | 88 | this.router.navigate(['/search'], { 89 | queryParams: { 90 | query: value.trim() 91 | }, 92 | queryParamsHandling: 'merge' 93 | }); 94 | } 95 | 96 | // applyFilter() { 97 | // this.bottomSheet.open(FilterPostsComponent); 98 | // } 99 | 100 | 101 | applyFilter(tag: string) { 102 | this.router.navigate(['/search'], { 103 | queryParams: { 104 | tag: tag 105 | }, 106 | queryParamsHandling: 'merge' 107 | }); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/app/models/comment.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface Comment { 3 | key: string, 4 | date: Date, 5 | text: string, 6 | replies: Replies[], 7 | } 8 | 9 | export interface Replies { 10 | key: string, 11 | date: Date, 12 | text: string, 13 | } -------------------------------------------------------------------------------- /src/app/models/feedback.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface Feedback { 3 | title: string; 4 | body: string; 5 | email?: string; 6 | } -------------------------------------------------------------------------------- /src/app/models/post.ts: -------------------------------------------------------------------------------- 1 | export class Post { 2 | key: string; 3 | title: string; 4 | body: string; 5 | tags: string[]; 6 | dateCreated: Date; 7 | email?: any; 8 | likes?: number; 9 | dislikes?: number; 10 | reports: number; 11 | comments?; 12 | liked?: string; 13 | disliked?: string; 14 | blurred?: string; 15 | raw_data: string; 16 | totalComments: number; 17 | } 18 | -------------------------------------------------------------------------------- /src/app/not-found/not-found.component.css: -------------------------------------------------------------------------------- 1 | * { 2 | transition: all 0.6s; 3 | } 4 | 5 | body { 6 | color: #888; 7 | margin: 0; 8 | } 9 | 10 | #main { 11 | margin-top: 70px; 12 | display: table; 13 | width: 100%; 14 | text-align: center; 15 | } 16 | 17 | .fof { 18 | display: block; 19 | margin-bottom: 60px; 20 | } 21 | 22 | .fof h1 { 23 | font-size: 50px; 24 | display: inline-block; 25 | padding-right: 12px; 26 | animation: type 0.5s alternate infinite; 27 | } 28 | 29 | .home { 30 | margin-top: 40px; 31 | } 32 | 33 | p { 34 | font-size: 20px; 35 | } 36 | 37 | @keyframes type { 38 | from { 39 | box-shadow: inset -3px 0px 0px #888; 40 | } 41 | to { 42 | box-shadow: inset -3px 0px 0px transparent; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app/not-found/not-found.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Error 404

4 |
5 | 6 |

7 | Seems like the page you're looking for doesn't exist. 8 |

9 | 10 |

11 | Sorry, we couldn't find anything matching your search criteria. 12 |

13 | 14 |

15 | Looks like this post doesn't exist or maybe it was deliberately 16 | removed due to excessive reports.

17 | Either way, we're sorry! 18 |

19 | 20 | 23 |
-------------------------------------------------------------------------------- /src/app/not-found/not-found.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NotFoundComponent } from './not-found.component'; 4 | 5 | describe('NotFoundComponent', () => { 6 | let component: NotFoundComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NotFoundComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NotFoundComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/not-found/not-found.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'not-found', 6 | templateUrl: './not-found.component.html', 7 | styleUrls: ['./not-found.component.css'] 8 | }) 9 | export class NotFoundComponent { 10 | 11 | @Input('search') search: boolean = false; 12 | 13 | fullPagePostError: boolean = false; 14 | 15 | constructor(private router: Router) { 16 | const url = this.router.url; 17 | 18 | if (url.includes('post') && !url.includes('feed') && !url.includes('popular') && !url.includes('about') && !url.includes('contact') && !url.includes('team')) 19 | this.fullPagePostError = true; 20 | else this.fullPagePostError = false; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/app/pipes/search-highlight.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { SearchHighlightPipe } from './search-highlight.pipe'; 2 | 3 | describe('SearchHighlightPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new SearchHighlightPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/app/pipes/search-highlight.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'searchHighlight', 5 | }) 6 | export class SearchHighlightPipe implements PipeTransform { 7 | transform(value: string, args: string[]): any { 8 | if (!args) return { value }; 9 | 10 | for (const searchTerm of args) { 11 | let regexText = new RegExp(searchTerm, 'gi'); 12 | value = value.replace( 13 | regexText, 14 | "$&" 15 | ); 16 | } 17 | 18 | let count = (value.match(new RegExp(args.toString(), 'gi')) || []).length; 19 | 20 | return { value, count }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/popular/popular.component.css: -------------------------------------------------------------------------------- 1 | @media (max-width: 992px) { 2 | .col-sm-0 { 3 | display: none; 4 | } 5 | } 6 | 7 | .caughtup { 8 | margin-bottom: 25px; 9 | border-radius: 4px; 10 | border: 1px solid grey; 11 | } 12 | 13 | .alert-dark { 14 | background-color: transparent; 15 | color: white; 16 | border: grey; 17 | } 18 | 19 | h1 { 20 | font-family: "Quicksand", sans-serif; 21 | } 22 | -------------------------------------------------------------------------------- /src/app/popular/popular.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 | 7 |
8 |
9 |
10 |

Here are the most popular posts for you.

11 |
12 |
13 |
14 | 15 |
16 |
17 |
You are all caught up! 18 |
19 | 20 |
21 |
22 |
23 | 24 |
25 |
26 |
-------------------------------------------------------------------------------- /src/app/popular/popular.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PopularComponent } from './popular.component'; 4 | 5 | describe('PopularComponent', () => { 6 | let component: PopularComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PopularComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PopularComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/popular/popular.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { take } from 'rxjs/operators'; 3 | import { PostService } from '../services/post.service'; 4 | import { AnalyticsService } from '../services/analytics.service'; 5 | import { Post } from '../models/post'; 6 | 7 | @Component({ 8 | selector: 'popular', 9 | templateUrl: './popular.component.html', 10 | styleUrls: ['./popular.component.css'] 11 | }) 12 | export class PopularComponent implements OnInit { 13 | posts: Post[]; 14 | isLoading: boolean = true; 15 | 16 | constructor( 17 | private postService: PostService, 18 | private analytics: AnalyticsService 19 | ) { 20 | this.postService.getAllPostsByLikes().pipe(take(1)).subscribe((post) => { 21 | this.posts = post.reverse(); 22 | this.isLoading = false; 23 | 24 | this.posts.forEach(item => { 25 | let like = localStorage.getItem('liked' + item.key); 26 | if (!like) item.liked = "false"; 27 | else item.liked = like; 28 | 29 | let dislike = localStorage.getItem('disliked' + item.key); 30 | if (!dislike) item.disliked = "false"; 31 | else item.disliked = dislike; 32 | }); 33 | }); 34 | } 35 | 36 | ngOnInit(): void { 37 | this.analytics 38 | .getCurrentPageVisits() 39 | .pipe(take(1)) 40 | .subscribe((item) => { 41 | this.analytics.updatePageVisits(item[0]); 42 | }); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/app/post-card/post-card.component.css: -------------------------------------------------------------------------------- 1 | .example-card { 2 | max-width: 1000px; 3 | } 4 | 5 | .blurred { 6 | filter: blur(10px); 7 | pointer-events: none; 8 | } 9 | 10 | mat-card { 11 | margin-bottom: 30px; 12 | background-color: black; 13 | } 14 | 15 | mat-card-header { 16 | padding-bottom: 0px; 17 | margin-bottom: 0px; 18 | height: auto; 19 | } 20 | 21 | mat-card-title { 22 | max-width: 100%; 23 | margin-right: 0px; 24 | cursor: pointer; 25 | word-break: break-word; 26 | overflow-wrap: break-word; 27 | overflow: hidden; 28 | } 29 | 30 | mat-card-title div { 31 | word-break: break-word; 32 | overflow-wrap: break-word; 33 | overflow: hidden; 34 | max-width: 100%; 35 | padding: 2px 0; 36 | } 37 | 38 | @media (min-width: 992px) { 39 | mat-card-title { 40 | max-width: 100%; 41 | } 42 | } 43 | 44 | @media (max-width: 375px) { 45 | mat-card-title { 46 | max-width: 100%; 47 | } 48 | } 49 | 50 | mat-card hr { 51 | border-radius: 1px solid #f9f9f9; 52 | margin: 0px 0px 10px 0px; 53 | } 54 | 55 | mat-card-content { 56 | font-size: 15px; 57 | } 58 | 59 | mat-card-content p { 60 | overflow: hidden; 61 | } 62 | 63 | .main-card-content { 64 | white-space: pre-line; 65 | } 66 | 67 | .main-card-content-short { 68 | cursor: pointer; 69 | } 70 | 71 | .mark-references { 72 | border-radius: 16px; 73 | padding: 4px 10px; 74 | font-weight: bold; 75 | background-color: cyan; 76 | cursor: pointer; 77 | color: #000000; 78 | text-align: center; 79 | } 80 | 81 | ::ng-deep .mat-card-title .post-title { 82 | font-size: 20px; 83 | } 84 | 85 | .main-card-content-short span { 86 | color: #ffc824; 87 | font-weight: bold; 88 | } 89 | 90 | .btn-group button { 91 | outline: none; 92 | border: none; 93 | background-color: transparent; 94 | color: whitesmoke; 95 | font-size: medium; 96 | } 97 | 98 | .share { 99 | outline: none; 100 | border: none; 101 | background-color: transparent; 102 | color: whitesmoke; 103 | /* margin-top: 6px; */ 104 | margin-right: 10px; 105 | padding-bottom: 2px; 106 | } 107 | 108 | .comment { 109 | margin-left: 14px; 110 | margin-top: 1px; 111 | margin-right: 5px; 112 | padding: 0 2px; 113 | } 114 | 115 | .comment-icon { 116 | margin-right: 5px; 117 | } 118 | 119 | .like { 120 | margin-top: 5px; 121 | cursor: pointer; 122 | } 123 | 124 | .like:hover { 125 | background-color: grey; 126 | } 127 | 128 | .dislike { 129 | margin-left: 15px; 130 | margin-top: 5px; 131 | } 132 | 133 | .liked { 134 | color: crimson; 135 | } 136 | 137 | .disliked { 138 | color: #ec9be3; 139 | } 140 | 141 | .comment mat-icon { 142 | color: #e4ea7d; 143 | } 144 | 145 | mat-card-header span { 146 | margin-right: 15px; 147 | font-size: 14px; 148 | } 149 | 150 | .float-right { 151 | margin-top: -2px; 152 | margin-right: 5px; 153 | } 154 | 155 | .mat-chip { 156 | color: black; 157 | background-color: yellow; 158 | } 159 | 160 | .tags { 161 | cursor: default; 162 | user-select: none; 163 | } 164 | 165 | .text-right { 166 | position: absolute; 167 | right: 0px; 168 | } 169 | -------------------------------------------------------------------------------- /src/app/post-card/post-card.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
This post has been tagged under NSFW and might contain adult content. Do you still want to view it?
4 | 5 |
6 |
7 | 8 | 9 | 10 | 11 |
15 | 16 |
17 |
18 | 19 | 21 | 29 | 30 | 61 | 63 | 64 | 65 | {{ post.dateCreated | date: "MMM d, yy   H:mm" }} 66 | 67 |
68 | 69 |
70 | 71 | 72 |

83 | 86 |

87 | 88 |

93 | 94 |

95 | 96 |
97 | 98 |

103 | {{ 104 | (post.body | searchHighlight: searchTerms).count === 1 105 | ? (post.body | searchHighlight: searchTerms).count + 106 | " reference found" 107 | : (post.body | searchHighlight: searchTerms).count + 108 | " references found" 109 | }} 110 | inside body 111 |

112 | 113 |
114 | 115 | 116 | 127 | {{ p }} 128 | 129 | 130 | 141 | + More 142 | 143 | 144 | 145 | 146 | 157 | {{ p }} 158 | 159 | 160 |
161 | 162 | 163 |
164 | 169 | 170 | 171 | 172 | 177 | 178 | 179 | 180 | 184 |
185 | 186 |
187 |
188 | 196 | 197 | 205 | 209 | 210 | 218 | 219 |
220 |
221 |
222 |
223 |
224 | -------------------------------------------------------------------------------- /src/app/post-card/post-card.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PostCardComponent } from './post-card.component'; 4 | 5 | describe('PostCardComponent', () => { 6 | let component: PostCardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PostCardComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PostCardComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/post-card/post-card.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { MatBottomSheet } from '@angular/material/bottom-sheet'; 3 | import { MatSnackBar } from '@angular/material/snack-bar'; 4 | import { Router } from '@angular/router'; 5 | 6 | import { Post } from '../models/post'; 7 | import { SharingOptionsComponent } from '../sharing-options/sharing-options.component'; 8 | import { PostService } from './../services/post.service'; 9 | import { $ } from 'protractor'; 10 | 11 | @Component({ 12 | selector: 'post-card', 13 | templateUrl: './post-card.component.html', 14 | styleUrls: ['./post-card.component.css'], 15 | }) 16 | export class PostCardComponent { 17 | @Input('post') post: Post; 18 | @Input('route') route: string; 19 | 20 | @Input('searchTerms') searchTerms: string[]; 21 | @Input('insideSearch') insideSearch: boolean; 22 | h: string; 23 | 24 | constructor( 25 | private router: Router, 26 | private snackBar: MatSnackBar, 27 | private bottomSheet: MatBottomSheet, 28 | private postService: PostService 29 | ) { } 30 | 31 | viewFullPost(key) { 32 | this.router.navigate(['/post', key]); 33 | } 34 | 35 | openSnackBar(action: string, uid?: string) { 36 | switch (action.toLowerCase()) { 37 | case 'copy': 38 | this.snackBar.open('Copied to clipboard!', 'Cool', { 39 | duration: 3000, 40 | }); 41 | break; 42 | 43 | case 'report': 44 | let reported = localStorage.getItem('reported' + uid); 45 | if (reported) { 46 | this.snackBar.open( 47 | 'You have already reported this post.', 48 | 'Okay', 49 | { 50 | duration: 6000, 51 | } 52 | ); 53 | } 54 | else { 55 | localStorage.setItem('reported' + uid, 'true'); 56 | this.snackBar.open( 57 | 'This post has been reported. Thanks for bringing it to our concern.', 58 | 'Good', 59 | { 60 | duration: 6000, 61 | } 62 | ); 63 | this.postService.reportPostById(uid); 64 | } 65 | break; 66 | 67 | default: this.snackBar.open('Seems like something went wrong', 'Dismiss'); 68 | } 69 | } 70 | 71 | openSharingOptions(key: string) { 72 | this.bottomSheet.open(SharingOptionsComponent, { 73 | data: { postKey: key }, 74 | backdropClass: 'backdrop', 75 | restoreFocus: true 76 | }); 77 | } 78 | 79 | updateLikes(key, event) { 80 | this.post.liked = (this.post.liked === "true") ? "false" : "true"; 81 | localStorage.setItem('liked' + key, this.post.liked); 82 | if (this.post.liked === "true") { 83 | this.postService.updateLike(key, 1); 84 | this.updateVisibleValues(event, 'likes', 'increase'); 85 | if (this.post.disliked === "true") { 86 | this.updateDislikes(key, event); 87 | this.cannotLikeAndDislike(); 88 | } 89 | } 90 | else { 91 | this.postService.updateLike(key, -1); 92 | this.updateVisibleValues(event, 'likes', 'decrease'); 93 | } 94 | } 95 | 96 | updateDislikes(key, event) { 97 | this.post.disliked = (this.post.disliked === "true") ? "false" : "true"; 98 | localStorage.setItem('disliked' + key, this.post.disliked); 99 | if (this.post.disliked === "true") { 100 | this.postService.updateDislike(key, 1); 101 | this.updateVisibleValues(event, 'dislikes', 'increase'); 102 | if (this.post.liked === "true") { 103 | this.updateLikes(key, event); 104 | this.cannotLikeAndDislike(); 105 | } 106 | } 107 | else { 108 | this.postService.updateDislike(key, -1); 109 | this.updateVisibleValues(event, 'dislikes', 'decrease'); 110 | } 111 | } 112 | 113 | updateVisibleValues(event, str: string, incdec: string) { 114 | let elementForLikes = event.currentTarget.parentElement.getElementsByClassName('numberlikes')[0]; 115 | let elementForDislikes = event.currentTarget.parentElement.getElementsByClassName('numberdislikes')[0]; 116 | 117 | if (str === 'likes') { 118 | if (incdec === 'increase') 119 | elementForLikes.innerHTML = ++this.post.likes; 120 | else 121 | elementForLikes.innerHTML = --this.post.likes; 122 | } 123 | 124 | else { 125 | if (incdec === 'increase') 126 | elementForDislikes.innerHTML = ++this.post.dislikes; 127 | else 128 | elementForDislikes.innerHTML = --this.post.dislikes; 129 | } 130 | } 131 | 132 | cannotLikeAndDislike() { 133 | this.snackBar.open( 134 | 'You cannot like and dislike the same post!', 135 | 'Okay', 136 | { 137 | duration: 3000, 138 | } 139 | ); 140 | } 141 | 142 | applyFilter(tag) { 143 | this.router.navigate(['/search'], { 144 | queryParams: { 145 | tag: tag 146 | }, 147 | queryParamsHandling: 'merge' 148 | }); 149 | } 150 | 151 | checkBlur(key, event) { 152 | if(!localStorage.getItem('blurred') + key) 153 | localStorage.setItem('blurred' + key, 'set'); 154 | let x = event.currentTarget.parentElement.parentElement.parentElement.getElementsByClassName('blurred')[0]; 155 | x.style.filter = 'unset'; 156 | x.style.pointerEvents = 'auto'; 157 | let y = event.currentTarget.parentElement.parentElement.parentElement.getElementsByClassName('warnblurred')[0]; 158 | y.style.display = 'none'; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/app/posts-filter/posts-filter.component.css: -------------------------------------------------------------------------------- 1 | .sticky-top { 2 | width: 100%; 3 | height: 520px; 4 | overflow-y: hidden; 5 | overflow-x: hidden; 6 | } 7 | 8 | .list-group { 9 | color: black; 10 | width: 100%; 11 | height: 100%; 12 | overflow-y: scroll; 13 | overflow-x: hidden; 14 | padding-right: 17px; 15 | box-sizing: content-box; 16 | } 17 | 18 | .list-group .list-group-item { 19 | background-color: transparent; 20 | color: white; 21 | border: 1px solid grey; 22 | user-select: none; 23 | transition: background-color 0.2s ease; 24 | } 25 | 26 | .list-group .list-group-item:hover { 27 | background-color: #ffc824; 28 | color: black; 29 | } 30 | 31 | .fixed { 32 | position: fixed; 33 | max-width: 250px; 34 | } 35 | -------------------------------------------------------------------------------- /src/app/posts-filter/posts-filter.component.html: -------------------------------------------------------------------------------- 1 |
2 | 4 | local_offer 5 | All Tags 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | {{ t.val.name }} 16 |
17 | 18 | 19 | 20 | 21 |
22 | Scroll for more   29 | arrow_downward 30 | 31 |
-------------------------------------------------------------------------------- /src/app/posts-filter/posts-filter.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PostsFilterComponent } from './posts-filter.component'; 4 | 5 | describe('PostsFilterComponent', () => { 6 | let component: PostsFilterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PostsFilterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PostsFilterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/posts-filter/posts-filter.component.ts: -------------------------------------------------------------------------------- 1 | import { Router } from '@angular/router'; 2 | import { PostService } from './../services/post.service'; 3 | import { Component, OnInit } from '@angular/core'; 4 | import { TagService } from '../services/tag.service'; 5 | 6 | @Component({ 7 | selector: 'posts-filter', 8 | templateUrl: './posts-filter.component.html', 9 | styleUrls: ['./posts-filter.component.css'] 10 | }) 11 | export class PostsFilterComponent implements OnInit { 12 | tags; 13 | isLoading: boolean = true; 14 | 15 | constructor(private tagService: TagService, private router: Router) { } 16 | 17 | ngOnInit(): void { 18 | this.tagService.getAll().subscribe(tags => { 19 | this.tags = tags; 20 | this.isLoading = false; 21 | }); 22 | } 23 | 24 | applyFilter(tag) { 25 | this.router.navigate(['/search'], { 26 | queryParams: { 27 | tag: tag 28 | }, 29 | queryParamsHandling: 'merge' 30 | }); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/app/posts/posts.component.css: -------------------------------------------------------------------------------- 1 | mat-icon { 2 | margin-left: -5px !important; 3 | } 4 | 5 | .mat-icon-stars { 6 | margin-bottom: 3px; 7 | } 8 | 9 | .mat-icon-schedule { 10 | margin-bottom: 3px; 11 | } 12 | 13 | .caughtup { 14 | margin-bottom: 25px; 15 | border-radius: 4px; 16 | border: 1px solid grey; 17 | } 18 | 19 | .recent { 20 | color: #EC9BE3; 21 | } 22 | 23 | .showbuttons { 24 | display: flex; 25 | justify-content: space-evenly; 26 | flex-wrap: wrap; 27 | } 28 | 29 | .topbtn { 30 | margin-top: 5px; 31 | } 32 | 33 | @media (max-width: 992px) { 34 | .col-sm-0 { 35 | display: none; 36 | } 37 | } 38 | 39 | .showbuttons button { 40 | font-weight: bold; 41 | } 42 | 43 | @media (min-width: 992px) { 44 | .showbuttons { 45 | display: none; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/posts/posts.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 9 | 13 | 17 |
18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 |
You are all caught up! 30 |
31 | 32 |
33 |
34 |
35 | 36 |
37 |
38 |
-------------------------------------------------------------------------------- /src/app/posts/posts.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PostsComponent } from './posts.component'; 4 | 5 | describe('PostsComponent', () => { 6 | let component: PostsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PostsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PostsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/posts/posts.component.ts: -------------------------------------------------------------------------------- 1 | import { Router } from '@angular/router'; 2 | import { PostService } from './../services/post.service'; 3 | import { Component, OnInit } from '@angular/core'; 4 | import { Post } from '../models/post'; 5 | import { AnalyticsService } from '../services/analytics.service'; 6 | import { take } from 'rxjs/operators'; 7 | 8 | @Component({ 9 | selector: 'app-posts', 10 | templateUrl: './posts.component.html', 11 | styleUrls: ['./posts.component.css'], 12 | }) 13 | export class PostsComponent implements OnInit { 14 | posts: Post[]; 15 | isLoading: boolean = true; 16 | 17 | constructor( 18 | private postService: PostService, 19 | private analytics: AnalyticsService, 20 | private route: Router, 21 | ) { 22 | 23 | this.postService.getAllPosts().pipe(take(1)).subscribe((post) => { 24 | this.posts = this.shuffle(post); 25 | this.isLoading = false; 26 | 27 | this.posts.forEach(item => { 28 | let like = localStorage.getItem('liked' + item.key); 29 | if (!like) item.liked = "false"; 30 | else item.liked = like; 31 | 32 | let dislike = localStorage.getItem('disliked' + item.key); 33 | if (!dislike) item.disliked = "false"; 34 | else item.disliked = dislike; 35 | }); 36 | }); 37 | } 38 | 39 | ngOnInit(): void { 40 | this.analytics 41 | .getCurrentPageVisits() 42 | .pipe(take(1)) 43 | .subscribe((item) => { 44 | this.analytics.updatePageVisits(item[0]); 45 | }); 46 | 47 | } 48 | 49 | private shuffle(array) { 50 | 51 | var currentIndex = array.length, temporaryValue, randomIndex; 52 | while (0 !== currentIndex) { 53 | 54 | randomIndex = Math.floor(Math.random() * currentIndex); 55 | currentIndex -= 1; 56 | 57 | temporaryValue = array[currentIndex]; 58 | array[currentIndex] = array[randomIndex]; 59 | array[randomIndex] = temporaryValue; 60 | } 61 | return array; 62 | } 63 | 64 | showPopular() { 65 | this.route.navigate(['popular']); 66 | } 67 | 68 | showRecent() { 69 | this.route.navigate(['recent']); 70 | } 71 | 72 | showTrending() { 73 | let max = 0, key = this.posts[0].key; 74 | for (let i = 0; i < this.posts.length; i++) { 75 | if (this.posts[i].totalComments > max && this.daysBetweenDates(this.posts[i].dateCreated, new Date().getTime()) < 5) { 76 | max = this.posts[i].totalComments; 77 | key = this.posts[i].key; 78 | } 79 | } 80 | this.route.navigate(['/post', key]); 81 | } 82 | 83 | daysBetweenDates(d1, d2) { 84 | var diff = d2 - d1; 85 | return diff / (1000 * 60 * 60 * 24); 86 | } 87 | 88 | revisit() { 89 | location.reload(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/app/privacy-policy/privacy-policy.component.css: -------------------------------------------------------------------------------- 1 | 2 | .container-policy { 3 | margin-top: 60px; 4 | max-width: 1000px; 5 | margin-left: auto; 6 | margin-right: auto; 7 | } 8 | 9 | .text { 10 | text-align: left; 11 | /* text-justify: newspaper; */ 12 | } 13 | 14 | .inner { 15 | display: flex; 16 | justify-content: center; 17 | margin-bottom: 45px; 18 | margin-top: -10px; 19 | } 20 | 21 | .logo-body { 22 | width: 178px; 23 | height: 178px; 24 | border-radius: 50%; 25 | } 26 | 27 | h1 { 28 | text-align: center; 29 | font-family: "Quicksand", sans-serif; 30 | margin-bottom: 50px; 31 | font-size: 26px; 32 | } 33 | 34 | .buttoncontact { 35 | margin: 40px auto 50px auto; 36 | text-align: center; 37 | } -------------------------------------------------------------------------------- /src/app/privacy-policy/privacy-policy.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | logo 5 |
6 | 7 |

POLICY STATEMENT

8 |
9 | Cloakspace is an example of anonymity where opinions are always welcome. No one should feel restrained here.

10 | 11 | However, although we are an anonymous site, we have to maintain some guidelines and consider these while determining which posts should be considered as inappropriate.

12 | 13 | We encourage everyone to share their opinions here without any fear but at the same time, we understand that this does not come with the freedom to type anything that might be extreme. Cloakspace neither promotes any hate speech, cyber bullying nor does it have any affiliation towards any group, community, society be it political or social. If we find any post which engages in activities including BUT NOT limited to using ethnic slurs, obscenity, racism, cyber violence, we reserve the right to remove that post permanently. We will look at each post objectively and adhere to the guidelines we have mentioned above. Extreme posts be it abusive or bullying, shall be removed if they disturb the order of the website.

14 | 15 | In addition to this, we have a report action which we urge people to use in case someone finds any post abusive or unusual. We would have a look at it as soon as we receive a report. You can also use the feedback to notify us of any indecent activities that might be going on to help us keep in track. You can also inform the creators directly.

16 | 17 | Cloakspace should remain an optimistic platform encouraging people to come forward with their thoughts and we shall always try to keep it that way.

18 | 19 | Stay happy. Keep posting! 20 |
21 |
23 |
-------------------------------------------------------------------------------- /src/app/privacy-policy/privacy-policy.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PrivacyPolicyComponent } from './privacy-policy.component'; 4 | 5 | describe('PrivacyPolicyComponent', () => { 6 | let component: PrivacyPolicyComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PrivacyPolicyComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PrivacyPolicyComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/privacy-policy/privacy-policy.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-privacy-policy', 5 | templateUrl: './privacy-policy.component.html', 6 | styleUrls: ['./privacy-policy.component.css'] 7 | }) 8 | export class PrivacyPolicyComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/recent/recent.component.css: -------------------------------------------------------------------------------- 1 | 2 | @media (max-width: 992px) { 3 | .col-sm-0 { 4 | display: none; 5 | } 6 | } 7 | 8 | .caughtup { 9 | margin-bottom: 25px; 10 | border-radius: 4px; 11 | border: 1px solid grey; 12 | } 13 | 14 | .alert-dark { 15 | background-color: transparent; 16 | color: white; 17 | border: grey; 18 | } 19 | 20 | h1 { 21 | font-family: "Quicksand", sans-serif; 22 | } -------------------------------------------------------------------------------- /src/app/recent/recent.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 | 8 |
9 |
10 |
11 |

Here are the most recent posts for you.

12 |
13 |
14 |
15 | 16 |
17 |
18 |
You are all caught up! 19 |
20 | 21 |
22 |
23 |
24 | 25 |
26 |
27 |
-------------------------------------------------------------------------------- /src/app/recent/recent.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { RecentComponent } from './recent.component'; 4 | 5 | describe('RecentComponent', () => { 6 | let component: RecentComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ RecentComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(RecentComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/recent/recent.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { take } from 'rxjs/operators'; 3 | 4 | import { Post } from '../models/post'; 5 | import { AnalyticsService } from '../services/analytics.service'; 6 | import { PostService } from '../services/post.service'; 7 | 8 | @Component({ 9 | selector: 'app-recent', 10 | templateUrl: './recent.component.html', 11 | styleUrls: ['./recent.component.css'] 12 | }) 13 | export class RecentComponent implements OnInit { 14 | posts: Post[]; 15 | isLoading: boolean = true; 16 | 17 | constructor( 18 | private postService: PostService, 19 | private analytics: AnalyticsService 20 | ) { 21 | this.postService.getAllPosts().pipe(take(1)).subscribe((post) => { 22 | this.posts = post.reverse(); 23 | this.isLoading = false; 24 | 25 | this.posts.forEach(item => { 26 | let like = localStorage.getItem('liked' + item.key); 27 | if (!like) item.liked = "false"; 28 | else item.liked = like; 29 | 30 | let dislike = localStorage.getItem('disliked' + item.key); 31 | if (!dislike) item.disliked = "false"; 32 | else item.disliked = dislike; 33 | }); 34 | }); 35 | } 36 | 37 | ngOnInit(): void { 38 | this.analytics 39 | .getCurrentPageVisits() 40 | .pipe(take(1)) 41 | .subscribe((item) => { 42 | this.analytics.updatePageVisits(item[0]); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/search/search.component.css: -------------------------------------------------------------------------------- 1 | .text-center { 2 | margin-top: 25px; 3 | margin-bottom: 40px; 4 | } 5 | 6 | .search-details { 7 | margin: 35px 0 55px; 8 | display: flex; 9 | justify-content: center; 10 | } 11 | 12 | ::ng-deep .filter-menu button { 13 | font-family: "Quicksand", sans-serif; 14 | } 15 | -------------------------------------------------------------------------------- /src/app/search/search.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |

8 | {{ filteredPosts.length }} {{ filteredPosts.length === 1 ? 'result' : 'results' }} for 9 | {{ queryParams.tag ? '"' + queryParams.tag + '"' : '' }} 10 | {{ queryParams.tag && queryParams.query ? 'and' : '' }} 11 | {{ queryParams.query ? '"' + queryParams.query + '"' : '' }} 12 |

13 | 14 |
15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 |
27 |
28 | 29 |
30 | 31 | 32 |
33 | 34 |
35 |
36 |
37 | 38 | 39 | 40 | 41 | 42 |
-------------------------------------------------------------------------------- /src/app/search/search.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SearchComponent } from './search.component'; 4 | 5 | describe('SearchComponent', () => { 6 | let component: SearchComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SearchComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SearchComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/search/search.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MatDialog } from '@angular/material/dialog'; 3 | import { ActivatedRoute, Router } from '@angular/router'; 4 | import { combineLatest } from 'rxjs'; 5 | 6 | import { FeedRedirectComponent } from '../feed-redirect/feed-redirect.component'; 7 | import { Post } from '../models/post'; 8 | import { PostService } from '../services/post.service'; 9 | 10 | @Component({ 11 | selector: 'app-search', 12 | templateUrl: './search.component.html', 13 | styleUrls: ['./search.component.css'] 14 | }) 15 | export class SearchComponent { 16 | 17 | posts: Post[]; 18 | filteredPosts: Post[]; 19 | isLoading: boolean = true; 20 | queryParams: any; 21 | 22 | searchTerms: string[]; 23 | insideSearch: boolean; 24 | 25 | constructor( 26 | private route: ActivatedRoute, 27 | private router: Router, 28 | private postService: PostService, 29 | private dialog: MatDialog 30 | ) { 31 | 32 | combineLatest( 33 | this.route.queryParams, 34 | this.postService.getAllPosts() 35 | ).subscribe(combinedObservable => { 36 | 37 | this.isLoading = true; 38 | 39 | if (Object.keys(combinedObservable[0]).length === 0) { 40 | this.router.navigate(['/']); 41 | return; 42 | } 43 | 44 | this.queryParams = combinedObservable[0]; 45 | this.posts = combinedObservable[1]; 46 | 47 | if (this.queryParams.query) { 48 | this.searchTerms = (this.queryParams.query).split(/\s+/); 49 | this.insideSearch = true; 50 | } 51 | else this.insideSearch = false; 52 | 53 | this.filterPosts(this.queryParams); 54 | 55 | this.isLoading = false; 56 | }); 57 | } 58 | 59 | clearFilters(action: string) { 60 | 61 | switch (action) { 62 | case 'query': 63 | if (this.queryParams.query && !this.queryParams.tag) 64 | this.dialog.open(FeedRedirectComponent, { 65 | width: '400px' 66 | }); 67 | else if (this.queryParams.query && this.queryParams.tag) 68 | this.router.navigate(['.'], { 69 | relativeTo: this.route, 70 | queryParams: { 71 | query: null 72 | }, 73 | queryParamsHandling: 'merge' 74 | }); 75 | this.searchTerms = []; 76 | break; 77 | 78 | case 'tag': 79 | if (this.queryParams.tag && !this.queryParams.query) 80 | this.dialog.open(FeedRedirectComponent, { 81 | width: '400px' 82 | }); 83 | else if (this.queryParams.tag && this.queryParams.query) 84 | this.router.navigate(['.'], { 85 | relativeTo: this.route, 86 | queryParams: { 87 | tag: null 88 | }, 89 | queryParamsHandling: 'merge' 90 | }); 91 | break; 92 | 93 | case 'all': 94 | this.dialog.open(FeedRedirectComponent, { 95 | width: '400px' 96 | }); 97 | break; 98 | } 99 | } 100 | 101 | private filterPosts({ query, tag }) { 102 | 103 | if (query && tag) { 104 | this.filteredPosts = this.posts 105 | .filter(post => post.raw_data.toLowerCase().includes((query as string).trim().toLowerCase())) 106 | .filter(post => post.tags.includes(tag)); 107 | } 108 | 109 | else if (query) 110 | this.filteredPosts = this.posts.filter(post => post.raw_data.toLowerCase().includes((query as string).toLowerCase())); 111 | 112 | else if (tag) 113 | this.filteredPosts = this.posts.filter(post => post.tags.includes(tag)); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/app/services/admin-authguard.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AdminAuthguardService } from './admin-authguard.service'; 4 | 5 | describe('AdminAuthguardService', () => { 6 | let service: AdminAuthguardService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(AdminAuthguardService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/services/admin-authguard.service.ts: -------------------------------------------------------------------------------- 1 | import { CanActivate, Router } from '@angular/router'; 2 | import { Injectable } from '@angular/core'; 3 | import { map } from 'rxjs/operators'; 4 | import { Observable } from 'rxjs'; 5 | import { AuthService } from './auth-service.service'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class AdminAuthguardService implements CanActivate { 11 | 12 | constructor(private auth: AuthService, private router: Router) { } 13 | 14 | canActivate(): Observable { 15 | return this.auth.getLoggedInUser().pipe(map((item => { 16 | if (item[2] === 'none') 17 | this.router.navigate(['/']); 18 | return item[2] === "none" ? false : true; 19 | }))); 20 | } 21 | } -------------------------------------------------------------------------------- /src/app/services/analytics.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AnalyticsService } from './analytics.service'; 4 | 5 | describe('AnalyticsService', () => { 6 | let service: AnalyticsService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(AnalyticsService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/services/analytics.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AngularFireDatabase } from '@angular/fire/database'; 3 | 4 | @Injectable({ 5 | providedIn: 'root', 6 | }) 7 | export class AnalyticsService { 8 | constructor(private db: AngularFireDatabase) {} 9 | 10 | updatePageVisits(x) { 11 | this.db.object('/analytics').update({ 12 | noOfPageVisits: x + 1, 13 | }); 14 | } 15 | 16 | getCurrentPageVisits() { 17 | return this.db.list('/analytics').valueChanges(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/services/auth-service.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AuthServiceService } from './auth-service.service'; 4 | 5 | describe('AuthServiceService', () => { 6 | let service: AuthServiceService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(AuthServiceService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/services/auth-service.service.ts: -------------------------------------------------------------------------------- 1 | import { AngularFireDatabase } from '@angular/fire/database'; 2 | import { Observable } from 'rxjs'; 3 | import { AngularFireAuth } from '@angular/fire/auth'; 4 | import { Injectable } from '@angular/core'; 5 | import * as firebase from 'firebase'; 6 | 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class AuthService { 12 | user$: Observable; 13 | 14 | constructor( private db: AngularFireDatabase, private aAuth: AngularFireAuth ) { 15 | this.user$ = this.aAuth.authState; 16 | } 17 | 18 | updateAccess(name) { 19 | return this.db.object('/').update({ 20 | loggedIn: name 21 | }); 22 | } 23 | 24 | getLoggedInUser() { 25 | return this.db.list('/').valueChanges(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/services/feedback-service.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { FeedbackServiceService } from './feedback-service.service'; 4 | 5 | describe('FeedbackServiceService', () => { 6 | let service: FeedbackServiceService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(FeedbackServiceService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/services/feedback-service.service.ts: -------------------------------------------------------------------------------- 1 | import { Feedback } from './../models/feedback'; 2 | import { AngularFireDatabase } from '@angular/fire/database'; 3 | import { Injectable } from '@angular/core'; 4 | import { Observable } from 'rxjs'; 5 | import { map } from 'rxjs/operators'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class FeedbackService { 11 | 12 | constructor(private db: AngularFireDatabase) { } 13 | 14 | createFeedback(feed: Feedback) { 15 | return this.db.list('/feedbacks').push({ 16 | title: feed.title, 17 | body: feed.body, 18 | dateCreated: new Date().getTime(), 19 | email: (feed.email) ? feed.email : 'not provided', 20 | }); 21 | } 22 | 23 | getAllFeedbacks(): Observable { 24 | return this.db 25 | .list('/feedbacks', (ref) => ref.orderByChild('dateCreated')) 26 | .snapshotChanges() 27 | .pipe( 28 | map((actions) => 29 | actions.map((action) => { 30 | return { 31 | key: action.key, 32 | title: action.payload.val()['title'], 33 | dateCreated: action.payload.val()['dateCreated'], 34 | body: action.payload.val()['body'], 35 | email: action.payload.val()['email'], 36 | }; 37 | }) 38 | ) 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/services/post.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { PostService } from './post.service'; 4 | 5 | describe('PostService', () => { 6 | let service: PostService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(PostService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/services/tag.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { TagService } from './tag.service'; 4 | 5 | describe('TagService', () => { 6 | let service: TagService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(TagService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/services/tag.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AngularFireDatabase } from '@angular/fire/database'; 3 | import { map } from 'rxjs/operators'; 4 | 5 | @Injectable({ 6 | providedIn: 'root', 7 | }) 8 | export class TagService { 9 | constructor(private db: AngularFireDatabase) { } 10 | 11 | getAll() { 12 | return this.db 13 | .list('/tags', (ref) => ref.orderByChild('name')) 14 | .snapshotChanges() 15 | .pipe( 16 | map((actions) => 17 | actions.map((action) => ({ 18 | key: action.key, 19 | val: action.payload.val(), 20 | })) 21 | ) 22 | ); 23 | } 24 | 25 | // For future use 26 | getTag() { } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/sharing-options/sharing-options.component.css: -------------------------------------------------------------------------------- 1 | .share-buttons { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | } 6 | 7 | .share-whatsApp img, 8 | .share-facebook img, 9 | .share-twitter img, 10 | .share-mail img { 11 | height: 35px; 12 | width: 35px; 13 | padding-bottom: 3px; 14 | } 15 | 16 | .share-buttons button { 17 | margin-right: 15px; 18 | } 19 | 20 | h1 { 21 | font-family: "Quicksand", sans-serif; 22 | } 23 | -------------------------------------------------------------------------------- /src/app/sharing-options/sharing-options.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |

6 | Sharing Options 7 |

8 | 9 | 27 | 28 |
29 |
30 |
-------------------------------------------------------------------------------- /src/app/sharing-options/sharing-options.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SharingOptionsComponent } from './sharing-options.component'; 4 | 5 | describe('SharingOptionsComponent', () => { 6 | let component: SharingOptionsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SharingOptionsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SharingOptionsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/sharing-options/sharing-options.component.ts: -------------------------------------------------------------------------------- 1 | import { MatSnackBar } from '@angular/material/snack-bar'; 2 | import { Component, Inject } from '@angular/core'; 3 | import { MatBottomSheet, MAT_BOTTOM_SHEET_DATA } from '@angular/material/bottom-sheet'; 4 | import { DeviceDetectorService } from "ngx-device-detector"; 5 | 6 | @Component({ 7 | selector: 'app-sharing-options', 8 | templateUrl: './sharing-options.component.html', 9 | styleUrls: ['./sharing-options.component.css'], 10 | }) 11 | export class SharingOptionsComponent { 12 | 13 | constructor( 14 | private bottomSheet: MatBottomSheet, 15 | @Inject(MAT_BOTTOM_SHEET_DATA) public data: any, 16 | private _snackbar: MatSnackBar, 17 | private deviceService: DeviceDetectorService) { } 18 | 19 | share(media: string) { 20 | this.bottomSheet.dismiss(); 21 | // console.log(this.data.postKey); 22 | let link = 'https://cloakspace.tech/post/' + this.data.postKey; 23 | link = encodeURIComponent(link); 24 | let share = ""; 25 | 26 | switch (media) { 27 | case 'whatsapp': 28 | if (this.deviceService.isMobile()) 29 | share = "https://wa.me/?text=" + link; 30 | else if (this.deviceService.isDesktop() 31 | || this.deviceService.isTablet()) 32 | share = "https://web.whatsapp.com/send?text=" + link; 33 | break; 34 | 35 | case 'twitter': 36 | share = 'https://twitter.com/intent/tweet?text=' + link; 37 | break; 38 | 39 | case 'facebook': 40 | share = "http://www.facebook.com/sharer/sharer.php?u=" + link + "&t=" + encodeURIComponent('Check out this post!'); 41 | break; 42 | 43 | case 'mail': 44 | share = "mailto:?body=" + link; 45 | break; 46 | 47 | default: 48 | this._snackbar.open('Seems to be some sort of problem. Try again later!', 'Okay', { 49 | duration: 5000, 50 | }); 51 | } 52 | 53 | window.open(share, '_blank'); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/spinner/spinner.component.css: -------------------------------------------------------------------------------- 1 | .sk-cube-grid { 2 | width: 40px; 3 | height: 40px; 4 | margin: 100px auto; 5 | } 6 | 7 | .sk-cube-grid .sk-cube { 8 | width: 33%; 9 | height: 33%; 10 | background-color: #fff; 11 | float: left; 12 | -webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; 13 | animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; 14 | } 15 | .sk-cube-grid .sk-cube1 { 16 | -webkit-animation-delay: 0.2s; 17 | animation-delay: 0.2s; 18 | } 19 | .sk-cube-grid .sk-cube2 { 20 | -webkit-animation-delay: 0.3s; 21 | animation-delay: 0.3s; 22 | } 23 | .sk-cube-grid .sk-cube3 { 24 | -webkit-animation-delay: 0.4s; 25 | animation-delay: 0.4s; 26 | } 27 | .sk-cube-grid .sk-cube4 { 28 | -webkit-animation-delay: 0.1s; 29 | animation-delay: 0.1s; 30 | } 31 | .sk-cube-grid .sk-cube5 { 32 | -webkit-animation-delay: 0.2s; 33 | animation-delay: 0.2s; 34 | } 35 | .sk-cube-grid .sk-cube6 { 36 | -webkit-animation-delay: 0.3s; 37 | animation-delay: 0.3s; 38 | } 39 | .sk-cube-grid .sk-cube7 { 40 | -webkit-animation-delay: 0s; 41 | animation-delay: 0s; 42 | } 43 | .sk-cube-grid .sk-cube8 { 44 | -webkit-animation-delay: 0.1s; 45 | animation-delay: 0.1s; 46 | } 47 | .sk-cube-grid .sk-cube9 { 48 | -webkit-animation-delay: 0.2s; 49 | animation-delay: 0.2s; 50 | } 51 | 52 | @-webkit-keyframes sk-cubeGridScaleDelay { 53 | 0%, 54 | 70%, 55 | 100% { 56 | -webkit-transform: scale3D(1, 1, 1); 57 | transform: scale3D(1, 1, 1); 58 | } 59 | 35% { 60 | -webkit-transform: scale3D(0, 0, 1); 61 | transform: scale3D(0, 0, 1); 62 | } 63 | } 64 | 65 | .text-center { 66 | margin-top: -60px; 67 | } 68 | 69 | @keyframes sk-cubeGridScaleDelay { 70 | 0%, 71 | 70%, 72 | 100% { 73 | -webkit-transform: scale3D(1, 1, 1); 74 | transform: scale3D(1, 1, 1); 75 | } 76 | 35% { 77 | -webkit-transform: scale3D(0, 0, 1); 78 | transform: scale3D(0, 0, 1); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/app/spinner/spinner.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 13 |

14 | {{ randomText }} 15 |

-------------------------------------------------------------------------------- /src/app/spinner/spinner.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SpinnerComponent } from './spinner.component'; 4 | 5 | describe('SpinnerComponent', () => { 6 | let component: SpinnerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SpinnerComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SpinnerComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/spinner/spinner.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'spinner', 5 | templateUrl: './spinner.component.html', 6 | styleUrls: ['./spinner.component.css'], 7 | }) 8 | export class SpinnerComponent { 9 | 10 | randomText: string = 'Getting your posts'; 11 | 12 | randomTexts: string[] = [ 13 | "Pay no attention to the man behind the curtain", 14 | "Follow the white rabbit", 15 | "The satellite is moving into position", 16 | "You're not in Bengal anymore", 17 | "Reticulating Splines", 18 | "The gods cannot contemplate your fate", 19 | "It's not you, it's me", 20 | "So, do you come here often?", 21 | "Counting backwards from infinity", 22 | "Rolling for initiative", 23 | "E.T. phone home", 24 | "May the force be with you", 25 | "Bravo 6, going dark", 26 | "Alright lads, keep it tight", 27 | "Here's looking at you, kid", 28 | "I'm gonna make you an offer you can't refuse", 29 | "Bond. James Bond.", 30 | "You're gonna need a bigger boat", 31 | "I'll be back", 32 | "Save the Green, Save the people!", 33 | "Open the pod bay doors, HAL", 34 | "Apna Sapna, Privacy Privacy", 35 | "Shaken, not stirred", 36 | "Parallel processors running perpendicular today", 37 | "Operation BlackBriar is in effect", 38 | "You can't handle the truth!", 39 | "Well, nobody's perfect", 40 | "Interference from lunar radiation", 41 | "Positron routers depleted", 42 | "Cyborg nanites infesting the server", 43 | "Astropneumatic oscillations inducing delays", 44 | "Increased sunspot activity", 45 | "Network packets were eaten by the terminator", 46 | "Network packets travelling uphill", 47 | "Trojan horse ran out of hay", 48 | "Change in the earth's rotational speed", 49 | "The servers sends their regards", 50 | "Firing up thrusters for increased exposure", 51 | "Houston, we hear you loud and clear", 52 | "Well, someone woke up on the wrong side of the bed", 53 | "Alright, alright, alright!", 54 | "This is not a DRILL!", 55 | "Smells like something's burning", 56 | "Solar flares disrupting data packets", 57 | "Looks like the server is having a bad day", 58 | "I am usually not this slow, believe me", 59 | "The server ate a clock, it's very time consuming", 60 | "Carpe Diem, user!", 61 | "Is this 221B, Baker Street?", 62 | "Country roads, take me to my feed", 63 | "Servers, assemble!", 64 | "Wormholes causing inter dimensional interference", 65 | "Engaging thermodynamic blasters in 3 .. 2 .. 1", 66 | "Unclogging the main transfer route", 67 | "Phew! So much traffic!", 68 | "Doppelgangers killing power in the server farm", 69 | "I'll be there for you when the data starts to pour", 70 | "Servers serving food today, not data", 71 | "The seas may be rough but I am the Captain", 72 | "Do you know that data is pronounced as data?", 73 | "You leave when I say you leave", 74 | "The stars are lost today", 75 | "By order of the Peaky Fookin' Servers!", 76 | 'Say "hello" to my little friend!', 77 | "I feel the need - the need for speed!", 78 | "Bazinga!", 79 | "Accio data!", 80 | "Someone call the Ministry of Magic!", 81 | "And the Oscar goes to ... well, not the server", 82 | "Runtime exception thrown by the Large Hadron Collider", 83 | "Life is a box full of chocolates", 84 | "I am the one who knocks. I am the data!", 85 | "East or West? Tubu and Naru Best!", 86 | "They see me loading, They waiting!", 87 | "Recombobulating Discombobulators", 88 | "Achieving Nirvana", 89 | "Entering a state of biomorphic hyperturbulence", 90 | "Plan Paris has been activated", 91 | "Rounding up the discarded data chambers", 92 | "Do not go gentle into that good night", 93 | "The wait is worth it, we assure you" 94 | ]; 95 | 96 | constructor() { 97 | this.randomText = this.randomTexts[Math.floor(Math.random() * (this.randomTexts.length - 0 + 1) + 0)]; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/app/trending/trending.component.css: -------------------------------------------------------------------------------- 1 | 2 | .fixed { 3 | position: fixed; 4 | } 5 | 6 | .button { 7 | margin: 7px 0; 8 | } 9 | 10 | .text-center { 11 | font-size: 20px; 12 | margin: 20px 0; 13 | user-select: none; 14 | } 15 | 16 | .totalposts { 17 | font-size: 15px; 18 | margin-top: 350px; 19 | } 20 | 21 | .mat-icon-stars { 22 | margin-bottom: 2px; 23 | } 24 | 25 | .recent { 26 | color: #EC9BE3; 27 | margin: 7px; 28 | } 29 | 30 | .mat-icon-schedule { 31 | margin-bottom: 2px; 32 | } 33 | -------------------------------------------------------------------------------- /src/app/trending/trending.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | What's hot ? 4 |

5 | 6 |
7 |
8 | 9 | 13 | 17 | 21 | 22 |
23 |
24 | 25 |
26 |

Total posts: {{ totalPosts }}

27 |

Total page visits: {{ totalPageVisits }}

28 |
29 |
-------------------------------------------------------------------------------- /src/app/trending/trending.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TrendingComponent } from './trending.component'; 4 | 5 | describe('TrendingComponent', () => { 6 | let component: TrendingComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TrendingComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TrendingComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/trending/trending.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { take } from 'rxjs/operators'; 4 | 5 | import { Post } from '../models/post'; 6 | import { AnalyticsService } from './../services/analytics.service'; 7 | import { PostService } from './../services/post.service'; 8 | 9 | @Component({ 10 | selector: 'trending', 11 | templateUrl: './trending.component.html', 12 | styleUrls: ['./trending.component.css'], 13 | }) 14 | export class TrendingComponent implements OnInit { 15 | posts: Post[]; 16 | totalPosts = 0; 17 | totalPageVisits = 0; 18 | 19 | hasLoaded: boolean = false; 20 | 21 | constructor( 22 | private router: Router, 23 | private postService: PostService, 24 | private analyticsService: AnalyticsService 25 | ) { 26 | this.postService.getAllPosts().subscribe((post) => { 27 | this.posts = post.reverse(); 28 | this.totalPosts = this.posts.length; 29 | 30 | this.posts.forEach((item) => { 31 | let like = localStorage.getItem('liked' + item.key); 32 | if (!like) item.liked = 'false'; 33 | else item.liked = like; 34 | 35 | let dislike = localStorage.getItem('disliked' + item.key); 36 | if (!dislike) item.disliked = 'false'; 37 | else item.disliked = dislike; 38 | }); 39 | }); 40 | } 41 | 42 | ngOnInit(): void { 43 | this.analyticsService 44 | .getCurrentPageVisits() 45 | .pipe(take(1)) 46 | .subscribe((x) => { 47 | this.totalPageVisits = x[0]; 48 | }); 49 | } 50 | 51 | showPopular() { 52 | this.router.navigate(['popular']); 53 | } 54 | 55 | showTrending() { 56 | let max = 0, 57 | key = this.posts[0].key; 58 | for (let i = 0; i < this.posts.length; i++) { 59 | if ( 60 | this.daysBetweenDates(this.posts[i].dateCreated, new Date().getTime()) > 61 | 5 62 | ) 63 | break; 64 | 65 | if (this.posts[i].totalComments > max) { 66 | max = this.posts[i].totalComments; 67 | key = this.posts[i].key; 68 | } 69 | } 70 | this.router.navigate(['/post', key]); 71 | } 72 | 73 | daysBetweenDates(d1, d2) { 74 | let diff = d2 - d1; 75 | return diff / (1000 * 60 * 60 * 24); 76 | } 77 | 78 | showRecent() { 79 | this.router.navigate(['recent']); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /src/assets/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /src/assets/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/favicon-16x16.png -------------------------------------------------------------------------------- /src/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/favicon-32x32.png -------------------------------------------------------------------------------- /src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/favicon.ico -------------------------------------------------------------------------------- /src/assets/feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/feed.png -------------------------------------------------------------------------------- /src/assets/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/home.png -------------------------------------------------------------------------------- /src/assets/img/IMG-subham.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/IMG-subham.jpg -------------------------------------------------------------------------------- /src/assets/img/IMG-subhranil.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/IMG-subhranil.jpg -------------------------------------------------------------------------------- /src/assets/img/github-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/github-light.png -------------------------------------------------------------------------------- /src/assets/img/icon-features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/icon-features.png -------------------------------------------------------------------------------- /src/assets/img/icon-secure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/icon-secure.png -------------------------------------------------------------------------------- /src/assets/img/icon-trending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/icon-trending.png -------------------------------------------------------------------------------- /src/assets/img/icons/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/icons/facebook.png -------------------------------------------------------------------------------- /src/assets/img/icons/gmail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/icons/gmail.png -------------------------------------------------------------------------------- /src/assets/img/icons/linkedin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/icons/linkedin.png -------------------------------------------------------------------------------- /src/assets/img/icons/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/icons/twitter.png -------------------------------------------------------------------------------- /src/assets/img/icons/whatsapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/icons/whatsapp.png -------------------------------------------------------------------------------- /src/assets/img/logo/logo-body-del.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/logo/logo-body-del.jpg -------------------------------------------------------------------------------- /src/assets/img/logo/logo-body.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/logo/logo-body.jpg -------------------------------------------------------------------------------- /src/assets/img/logo/logo-navbar-del.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/logo/logo-navbar-del.png -------------------------------------------------------------------------------- /src/assets/img/logo/logo-navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/logo/logo-navbar.png -------------------------------------------------------------------------------- /src/assets/img/self-image-circle-min-Subham.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/self-image-circle-min-Subham.png -------------------------------------------------------------------------------- /src/assets/img/self-image-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/img/self-image-circle.png -------------------------------------------------------------------------------- /src/assets/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/mstile-150x150.png -------------------------------------------------------------------------------- /src/assets/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/assets/search.png -------------------------------------------------------------------------------- /src/assets/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloakspace", 3 | "short_name": "cloakspace", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /src/assets/smtp.js: -------------------------------------------------------------------------------- 1 | /* SmtpJS.com - v3.0.0 */ 2 | export var Email = { 3 | send: function (a) { 4 | return new Promise(function (n, e) { 5 | (a.nocache = Math.floor(1e6 * Math.random() + 1)), (a.Action = "Send"); 6 | var t = JSON.stringify(a); 7 | Email.ajaxPost("https://smtpjs.com/v3/smtpjs.aspx?", t, function (e) { 8 | n(e); 9 | }); 10 | }); 11 | }, 12 | ajaxPost: function (e, n, t) { 13 | var a = Email.createCORSRequest("POST", e); 14 | a.setRequestHeader("Content-type", "application/x-www-form-urlencoded"), 15 | (a.onload = function () { 16 | var e = a.responseText; 17 | null != t && t(e); 18 | }), 19 | a.send(n); 20 | }, 21 | ajax: function (e, n) { 22 | var t = Email.createCORSRequest("GET", e); 23 | (t.onload = function () { 24 | var e = t.responseText; 25 | null != n && n(e); 26 | }), 27 | t.send(); 28 | }, 29 | createCORSRequest: function (e, n) { 30 | var t = new XMLHttpRequest(); 31 | return ( 32 | "withCredentials" in t 33 | ? t.open(e, n, !0) 34 | : "undefined" != typeof XDomainRequest 35 | ? (t = new XDomainRequest()).open(e, n) 36 | : (t = null), 37 | t 38 | ); 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /src/assets/version-history.txt: -------------------------------------------------------------------------------- 1 | v1.0.0 -- Initial deployed state. 2 | v1.0.1 -- Fixed bugs related to alignment. Changed layout of the post-card. 3 | v1.1.0 -- Added policy. 4 | v1.1.1 -- Fixed bug in trending posts. 5 | v1.2.0 -- Added new feature: Replying to comments. 6 | v1.3.0 -- Added new feature: Notification alerts for new posts and comments. 7 | v1.3.1 -- Changed the width of the reply field in comments. 8 | v1.4.0 -- Added emoji support for large screens. 9 | v1.4.1 -- Fixed bug in comments, changed message while entering email. 10 | v1.4.2 -- Fixed bug in post-card title where all words were breaking. 11 | v1.5.2 -- Blurred all NSFW tagged posts. 12 | v1.5.3 -- Fixed alignment. -------------------------------------------------------------------------------- /src/custom-theme.scss: -------------------------------------------------------------------------------- 1 | @import "~@angular/material/theming"; 2 | @import '~@ctrl/ngx-emoji-mart/picker'; 3 | 4 | @include mat-core(); 5 | 6 | /** 7 | * 8 | * Default (Light) Theme 9 | * 10 | */ 11 | 12 | $kratos-primary: mat-palette($mat-deep-purple); 13 | $kratos-accent: mat-palette($mat-amber, A200, A100, A400); 14 | $kratos-warn: mat-palette($mat-red); 15 | 16 | $kratos-theme: mat-light-theme($kratos-primary, $kratos-accent, $kratos-warn); 17 | 18 | @include angular-material-theme($kratos-theme); 19 | 20 | /** 21 | * 22 | * Dark Theme 23 | * 24 | */ 25 | 26 | $kratos-alt-primary: mat-palette($mat-purple); 27 | $kratos-alt-accent: mat-palette($mat-lime, A200, A100, A400); 28 | $kratos-alt-warn: mat-palette($mat-deep-orange); 29 | 30 | $kratos-alt-theme: mat-dark-theme( 31 | $kratos-alt-primary, 32 | $kratos-alt-accent, 33 | $kratos-alt-warn 34 | ); 35 | 36 | .dark { 37 | @include angular-material-theme($kratos-alt-theme); 38 | } 39 | -------------------------------------------------------------------------------- /src/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /src/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /src/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /src/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /src/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/favicon/favicon.ico -------------------------------------------------------------------------------- /src/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/das-jishu/cloakspace/a08806ed1a739c29195f7f54bffb76e3958fba92/src/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /src/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloakspace", 3 | "short_name": "cloakspace", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cloakspace 7 | 8 | 9 | 10 | 11 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. 3 | */ 4 | import '@angular/localize/init'; 5 | /** 6 | * This file includes polyfills needed by Angular and is loaded before the app. 7 | * You can add your own extra polyfills to this file. 8 | * 9 | * This file is divided into 2 sections: 10 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 11 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 12 | * file. 13 | * 14 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 15 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 16 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 17 | * 18 | * Learn more in https://angular.io/guide/browser-support 19 | */ 20 | 21 | /*************************************************************************************************** 22 | * BROWSER POLYFILLS 23 | */ 24 | 25 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 26 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 27 | 28 | /** 29 | * Web Animations `@angular/platform-browser/animations` 30 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 31 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 32 | */ 33 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 34 | 35 | /** 36 | * By default, zone.js will patch all possible macroTask and DomEvents 37 | * user can disable parts of macroTask/DomEvents patch by setting following flags 38 | * because those flags need to be set before `zone.js` being loaded, and webpack 39 | * will put import in the top of bundle, so user need to create a separate file 40 | * in this directory (for example: zone-flags.ts), and put the following flags 41 | * into that file, and then add the following code before importing zone.js. 42 | * import './zone-flags'; 43 | * 44 | * The flags allowed in zone-flags.ts are listed here. 45 | * 46 | * The following flags will work for all browsers. 47 | * 48 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 49 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 50 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 51 | * 52 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 53 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 54 | * 55 | * (window as any).__Zone_enable_cross_context_check = true; 56 | * 57 | */ 58 | 59 | /*************************************************************************************************** 60 | * Zone JS is required by default for Angular itself. 61 | */ 62 | import 'zone.js/dist/zone'; // Included with Angular CLI. 63 | 64 | 65 | /*************************************************************************************************** 66 | * APPLICATION IMPORTS 67 | */ 68 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | @import "../node_modules/bootstrap/dist/css/bootstrap.css"; 4 | 5 | * { 6 | font-family: "Quicksand", sans-serif; 7 | letter-spacing: 1px; 8 | font-size: 14px; 9 | scroll-behavior: smooth; 10 | } 11 | 12 | html, 13 | body { 14 | min-height: 100vh; 15 | height: auto; 16 | } 17 | body { 18 | margin: 0; 19 | } 20 | 21 | .container { 22 | padding-top: 80px; 23 | width: 90vw; 24 | min-height: 100vh; 25 | } 26 | 27 | button:focus { 28 | outline: none; 29 | } 30 | 31 | .backdrop { 32 | background: #303030; 33 | } 34 | 35 | .search-text-highlight { 36 | color: #000000; 37 | background-color: rgb(248, 248, 142); 38 | font-size: inherit; 39 | font-weight: 700; 40 | } 41 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | // Then we find all the tests. 23 | const context = require.context('./', true, /\.spec\.ts$/); 24 | // And load the modules. 25 | context.keys().map(context); 26 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "lib": [ 15 | "es2018", 16 | "dom" 17 | ] 18 | }, 19 | "angularCompilerOptions": { 20 | "fullTemplateTypeCheck": true, 21 | "strictInjectionParameters": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ], 18 | /* "angularCompilerOptions": { 19 | "enableIvy": false 20 | } */ 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "align": { 5 | "options": [ 6 | "parameters", 7 | "statements" 8 | ] 9 | }, 10 | "array-type": false, 11 | "arrow-return-shorthand": true, 12 | "curly": true, 13 | "deprecation": { 14 | "severity": "warning" 15 | }, 16 | "component-class-suffix": true, 17 | "contextual-lifecycle": true, 18 | "directive-class-suffix": true, 19 | "directive-selector": [ 20 | true, 21 | "attribute", 22 | "app", 23 | "camelCase" 24 | ], 25 | "component-selector": [ 26 | true, 27 | "element", 28 | "app", 29 | "kebab-case" 30 | ], 31 | "eofline": true, 32 | "import-blacklist": [ 33 | true, 34 | "rxjs/Rx" 35 | ], 36 | "import-spacing": true, 37 | "indent": { 38 | "options": [ 39 | "spaces" 40 | ] 41 | }, 42 | "max-classes-per-file": false, 43 | "max-line-length": [ 44 | true, 45 | 140 46 | ], 47 | "member-ordering": [ 48 | true, 49 | { 50 | "order": [ 51 | "static-field", 52 | "instance-field", 53 | "static-method", 54 | "instance-method" 55 | ] 56 | } 57 | ], 58 | "no-console": [ 59 | true, 60 | "debug", 61 | "info", 62 | "time", 63 | "timeEnd", 64 | "trace" 65 | ], 66 | "no-empty": false, 67 | "no-inferrable-types": [ 68 | true, 69 | "ignore-params" 70 | ], 71 | "no-non-null-assertion": true, 72 | "no-redundant-jsdoc": true, 73 | "no-switch-case-fall-through": true, 74 | "no-var-requires": false, 75 | "object-literal-key-quotes": [ 76 | true, 77 | "as-needed" 78 | ], 79 | "quotemark": [ 80 | true, 81 | "single" 82 | ], 83 | "semicolon": { 84 | "options": [ 85 | "always" 86 | ] 87 | }, 88 | "space-before-function-paren": { 89 | "options": { 90 | "anonymous": "never", 91 | "asyncArrow": "always", 92 | "constructor": "never", 93 | "method": "never", 94 | "named": "never" 95 | } 96 | }, 97 | "typedef-whitespace": { 98 | "options": [ 99 | { 100 | "call-signature": "nospace", 101 | "index-signature": "nospace", 102 | "parameter": "nospace", 103 | "property-declaration": "nospace", 104 | "variable-declaration": "nospace" 105 | }, 106 | { 107 | "call-signature": "onespace", 108 | "index-signature": "onespace", 109 | "parameter": "onespace", 110 | "property-declaration": "onespace", 111 | "variable-declaration": "onespace" 112 | } 113 | ] 114 | }, 115 | "variable-name": { 116 | "options": [ 117 | "ban-keywords", 118 | "check-format", 119 | "allow-pascal-case" 120 | ] 121 | }, 122 | "whitespace": { 123 | "options": [ 124 | "check-branch", 125 | "check-decl", 126 | "check-operator", 127 | "check-separator", 128 | "check-type", 129 | "check-typecast" 130 | ] 131 | }, 132 | "no-conflicting-lifecycle": true, 133 | "no-host-metadata-property": true, 134 | "no-input-rename": true, 135 | "no-inputs-metadata-property": true, 136 | "no-output-native": true, 137 | "no-output-on-prefix": true, 138 | "no-output-rename": true, 139 | "no-outputs-metadata-property": true, 140 | "template-banana-in-box": true, 141 | "template-no-negated-async": true, 142 | "use-lifecycle-interface": true, 143 | "use-pipe-transform-interface": true 144 | }, 145 | "rulesDirectory": [ 146 | "codelyzer" 147 | ] 148 | } --------------------------------------------------------------------------------