├── .nvmrc ├── src ├── assets │ ├── .gitkeep │ ├── logo.png │ ├── favicon.ico │ ├── icons │ │ ├── icon.xcf │ │ ├── icon-72x72.png │ │ ├── icon-96x96.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ └── icon-512x512.png │ ├── codingmarks-logo.png │ ├── codingmarks-logo.xcf │ ├── img │ │ ├── user-generic.jpeg │ │ ├── gravatar-default.jpg │ │ ├── search-bar-example.png │ │ ├── public-vs-private-bookmarks.png │ │ └── public-vs-private-bookmarks-big.png │ ├── bookmarks.dev.logo.xcf │ ├── codingmarks-favicon.ico │ ├── codingmarks-logo-md.png │ ├── bookmarks-dev-context.png │ ├── bookmarks.dev-logo-md.png │ ├── fb-twitter-bookmarks.dev.png │ └── fb-twitter-bookmarks.dev.xcf ├── app │ ├── public │ │ ├── howto │ │ │ ├── howto.component.scss │ │ │ ├── howto.component.ts │ │ │ └── howto.component.spec.ts │ │ ├── bookmarklets │ │ │ ├── bookmarklet.component.scss │ │ │ ├── bookmarklet.component.ts │ │ │ └── bookmarklet.component.spec.ts │ │ ├── tag │ │ │ ├── tag.component.css │ │ │ ├── tag.component.spec.ts │ │ │ ├── tag.service.ts │ │ │ └── tag.component.html │ │ ├── about │ │ │ ├── about.component.scss │ │ │ ├── about.component.ts │ │ │ ├── about.component.spec.ts │ │ │ └── about.component.html │ │ ├── privacy │ │ │ ├── privacy-policy.component.scss │ │ │ ├── privacy-policy.component.ts │ │ │ └── privacy-policy.component.spec.ts │ │ ├── terms │ │ │ ├── terms-of-service.component.scss │ │ │ ├── terms-of-service.component.ts │ │ │ └── terms-of-service.component.spec.ts │ │ ├── search │ │ │ ├── bookmark-search.component.scss │ │ │ └── bookmarks-search.component.html │ │ ├── user-public-profile │ │ │ ├── user-public-profile.component.scss │ │ │ ├── user-public-profile.component.spec.ts │ │ │ └── user-public.service.ts │ │ ├── bookmarks │ │ │ ├── homepage.component.scss │ │ │ ├── public-bookmarks.service.ts │ │ │ └── store │ │ │ │ └── public-bookmarks-store.service.ts │ │ ├── public.module.ts │ │ └── public-routing.module.ts │ ├── user-settings │ │ ├── user-settings.component.scss │ │ ├── user-profile │ │ │ ├── user-profile.component.scss │ │ │ ├── image-upload.service.ts │ │ │ └── user-profile.component.spec.ts │ │ ├── user-settings.component.spec.ts │ │ ├── user-settings.component.ts │ │ ├── user-settings.component.html │ │ └── user-settings.module.ts │ ├── user │ │ └── dashboard │ │ │ ├── user-dashboard.component.scss │ │ │ ├── followers │ │ │ ├── followers.component.scss │ │ │ ├── followers.component.ts │ │ │ ├── followers.component.spec.ts │ │ │ └── followers.component.html │ │ │ ├── following │ │ │ ├── following.component.scss │ │ │ ├── following.component.spec.ts │ │ │ ├── following.component.ts │ │ │ └── following.component.html │ │ │ ├── user-bookmarks │ │ │ ├── user-bookmarks.component.scss │ │ │ ├── user-bookmarks.component.spec.ts │ │ │ ├── user-bookmarks.component.html │ │ │ └── user-bookmarks.component.ts │ │ │ ├── saved-searches │ │ │ ├── delete-saved-search-dialog │ │ │ │ ├── delete-saved-search-dialog.component.scss │ │ │ │ ├── delete-saved-search-dialog.component.html │ │ │ │ └── delete-saved-search-dialog.component.ts │ │ │ ├── saved-searches.component.scss │ │ │ ├── saved-searches.component.spec.ts │ │ │ ├── saved-searches.component.html │ │ │ └── saved-searches.component.ts │ │ │ ├── tags │ │ │ ├── delete-bookmarks-by-tag-dialog │ │ │ │ ├── delete-bookmarks-by-tag-dialog.component.scss │ │ │ │ ├── delete-bookmarks-by-tag-dialog.component.html │ │ │ │ └── delete-bookmarks-by-tag-dialog.component.ts │ │ │ ├── user-tags.component.scss │ │ │ └── user-tags.component.spec.ts │ │ │ ├── user-dashboard.component.spec.ts │ │ │ ├── user-dashboard.component.ts │ │ │ ├── user-dashboard.component.html │ │ │ └── user-dashboard.module.ts │ ├── codelet │ │ ├── codelet-details │ │ │ ├── codelet-details.component.scss │ │ │ ├── codelet-details.component.spec.ts │ │ │ ├── codelet-details.component.ts │ │ │ └── codelet-details.component.html │ │ ├── async-codelet-list │ │ │ ├── async-codelet-list.component.scss │ │ │ ├── codelet-code-snippets │ │ │ │ ├── codelet-card-body.component.scss │ │ │ │ ├── codelet-card-body.component.ts │ │ │ │ └── codelet-card-body.component.html │ │ │ ├── async-codelet-list.component.spec.ts │ │ │ ├── async-codelet-list.component.ts │ │ │ └── async-codelet-list.component.html │ │ ├── copy-snippet-button │ │ │ ├── copy-snippet-button.component.scss │ │ │ ├── copy-snippet-button.component.html │ │ │ ├── copy-snippet-button.component.spec.ts │ │ │ └── copy-snippet-button.component.ts │ │ ├── delete-codelet-dialog │ │ │ ├── delete-codelet-dialog.component.scss │ │ │ ├── delete-codelet-dialog.component.html │ │ │ ├── delete-bookmark-dialog.component.spec.ts │ │ │ └── delete-codelet-dialog.component.ts │ │ ├── update │ │ │ ├── update-codelet.component.html │ │ │ ├── update-codelet.component.scss │ │ │ └── update-codelet.component.ts │ │ ├── create │ │ │ ├── create-codelet.component.html │ │ │ ├── create-codelet.component.scss │ │ │ └── create-codelet.component.ts │ │ ├── save-codelet-form │ │ │ └── save-codelet-form.component.scss │ │ └── codelet.module.ts │ ├── core │ │ ├── model │ │ │ ├── pagination-action.ts │ │ │ ├── webpage-info.ts │ │ │ ├── used-tag.ts │ │ │ ├── user-data-profile.ts │ │ │ ├── user-info.oidc.ts │ │ │ ├── user-public-data.ts │ │ │ ├── rate-bookmark.request.ts │ │ │ ├── tags.ts │ │ │ ├── codelet.ts │ │ │ ├── bookmark.ts │ │ │ └── user-data.ts │ │ ├── error │ │ │ ├── error.model.ts │ │ │ ├── error.service.ts │ │ │ ├── error.component.html │ │ │ └── error.component.ts │ │ ├── logger.service.ts │ │ ├── loader │ │ │ ├── loader.service.ts │ │ │ ├── loader.service.spec.ts │ │ │ ├── loader-interceptor.service.spec.ts │ │ │ └── loader-interceptor.service.ts │ │ ├── navigation │ │ │ ├── navigation.component.scss │ │ │ └── navigation.component.ts │ │ ├── user │ │ │ ├── notify-stores.service.ts │ │ │ ├── user-info.service.ts │ │ │ ├── user-info.store.ts │ │ │ ├── suggested-tags.store.ts │ │ │ ├── feed-store.service.ts │ │ │ └── userdata.watched-tags.store.ts │ │ ├── pagination-notification.service.ts │ │ ├── validators │ │ │ └── text-size.validator.ts │ │ ├── keycloak-service-wrapper.service.ts │ │ ├── admin │ │ │ └── admin.service.ts │ │ ├── webpage-info │ │ │ └── webpage-info.service.ts │ │ ├── auth │ │ │ └── auth-guard.service.ts │ │ └── personal-codelets.service.ts │ ├── shared │ │ ├── social-share-dialog │ │ │ ├── social-share-dialog.component.scss │ │ │ ├── social-share-dialog.component.html │ │ │ └── social-share-dialog.component.ts │ │ ├── delete-bookmark-dialog │ │ │ ├── delete-bookmark-dialog.component.scss │ │ │ ├── delete-bookmark-dialog.component.spec.ts │ │ │ ├── delete-bookmark-dialog.component.html │ │ │ └── delete-bookmark-dialog.component.ts │ │ ├── login-required-dialog │ │ │ ├── login-required-dialog.component.scss │ │ │ ├── login-required-dialog.component.html │ │ │ └── login-required-dialog.component.ts │ │ ├── loader │ │ │ ├── loader.component.html │ │ │ ├── loader.component.css │ │ │ └── loader.component.ts │ │ ├── play-youtube-video-dialog │ │ │ ├── play-youtube-video-dialog.component.scss │ │ │ ├── play-youtube-video-dialog.component.html │ │ │ ├── play-youtube-video-dialog.component.spec.ts │ │ │ └── play-youtube-video-dialog.component.ts │ │ ├── bookmark-text.component.scss │ │ ├── bookmark-text.component.html │ │ ├── highlight.no-html-tags.pipe.ts │ │ ├── async-bookmark-list.component.scss │ │ ├── highlight.pipe.ts │ │ ├── codelet-common-tags.ts │ │ ├── bookmark-text.component.ts │ │ ├── highlight.pipe.spec.ts │ │ ├── tags-validation.directive.ts │ │ ├── tag-following-base-component │ │ │ └── tag-following-base.component.ts │ │ ├── shared.module.ts │ │ └── code-snippet-language-options.ts │ ├── personal │ │ ├── update │ │ │ ├── update-personal-bookmark.component.html │ │ │ ├── update-personal-bookmark.component.scss │ │ │ └── update-personal-bookmark.component.ts │ │ ├── save-bookmark-form │ │ │ ├── public-bookmark-present-dialog │ │ │ │ ├── public-bookmark-present-dialog.component.scss │ │ │ │ ├── public-bookmark-present-dialog.component.html │ │ │ │ └── public-bookmark-present-dialog.component.ts │ │ │ └── save-bookmark-form.component.scss │ │ ├── copy-to-mine │ │ │ ├── copy-to-mine-bookmark.component.html │ │ │ ├── copy-to-min-bookmark.component.scss │ │ │ └── copy-to-mine-bookmark.component.ts │ │ ├── create │ │ │ ├── create-personal-bookmark.component.html │ │ │ ├── create-personal-bookmark.component.scss │ │ │ └── create-personal-bookmark.component.ts │ │ ├── personal-bookmarks.component.ts │ │ ├── markdown.service.ts │ │ ├── markdown.service.spec.ts │ │ ├── personal-bookmarks-routing.module.ts │ │ └── personal-bookmarks.module.ts │ ├── app.component.scss │ ├── not-found.component.ts │ ├── app.component.ts │ ├── app.service.ts │ ├── social-buttons │ │ ├── social-buttons.module.ts │ │ ├── fb-share.component.ts │ │ └── tweet.component.ts │ ├── app.component.e2e-spec.js │ ├── app.component.html │ ├── app.routing.ts │ └── app-init.ts ├── typings.d.ts ├── _variables.scss ├── tsconfig.app.json ├── tsconfig.spec.json ├── main.ts ├── environments │ ├── environment.ts │ ├── environment.non-docker.ts │ ├── environment.prod.ts │ └── environment.prod-backend.local-testing.ts ├── test.ts ├── manifest.json ├── index.html └── polyfills.ts ├── temp.md ├── documentation ├── graphviz │ ├── components-graph.png │ ├── how-to-generate-png.md │ └── components-graph.gv ├── chrome-extension │ └── img │ │ ├── tiles-chorme-extension-for-store.png │ │ ├── screenshot-chorme-extension-for-store.png │ │ └── screenshot-chrom-extension-explanation.png └── regression-tests.yaml ├── .editorconfig ├── tsconfig.json ├── .gitignore ├── ngsw-config.json ├── protractor.conf.js ├── LICENSE ├── karma.conf.js └── package.json /.nvmrc: -------------------------------------------------------------------------------- 1 | 10.15.0 2 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/public/howto/howto.component.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/user-settings/user-settings.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/user/dashboard/user-dashboard.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/user/dashboard/followers/followers.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/codelet/codelet-details/codelet-details.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/public/bookmarklets/bookmarklet.component.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/user/dashboard/following/following.component.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/codelet/async-codelet-list/async-codelet-list.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/codelet/copy-snippet-button/copy-snippet-button.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /temp.md: -------------------------------------------------------------------------------- 1 | ## pagination 2 | 1. Next gets disabled when I delete a bookmark.... 3 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/app/core/model/pagination-action.ts: -------------------------------------------------------------------------------- 1 | export interface PaginationAction { 2 | caller: string; 3 | page: number; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/user/dashboard/user-bookmarks/user-bookmarks.component.scss: -------------------------------------------------------------------------------- 1 | .my-bookmarks-btn-group { 2 | margin-top: 1rem; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/favicon.ico -------------------------------------------------------------------------------- /src/app/public/tag/tag.component.css: -------------------------------------------------------------------------------- 1 | .tagged_header_wrapper { 2 | display: flex; 3 | justify-content: space-between; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/shared/social-share-dialog/social-share-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .app-dialog-actions { 2 | margin-bottom: 0.1rem; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/codelet/delete-codelet-dialog/delete-codelet-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .app-dialog-actions { 2 | margin-bottom: 0.1rem; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/core/error/error.model.ts: -------------------------------------------------------------------------------- 1 | export class Error { 2 | constructor(public title: string, public messages: string[]) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/app/shared/delete-bookmark-dialog/delete-bookmark-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .app-dialog-actions { 2 | margin-bottom: 0.1rem; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/shared/login-required-dialog/login-required-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .app-dialog-actions { 2 | margin-bottom: 0.1rem; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/icons/icon.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/icons/icon.xcf -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/_variables.scss: -------------------------------------------------------------------------------- 1 | $icon-font-path: '~bootstrap-sass/assets/fonts/bootstrap/'; 2 | $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; 3 | -------------------------------------------------------------------------------- /src/app/codelet/update/update-codelet.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/public/about/about.component.scss: -------------------------------------------------------------------------------- 1 | .bookmarks-context { 2 | display: flex; 3 | justify-content: center; 4 | margin-top: 2rem; 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/codingmarks-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/codingmarks-logo.png -------------------------------------------------------------------------------- /src/assets/codingmarks-logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/codingmarks-logo.xcf -------------------------------------------------------------------------------- /src/assets/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/icons/icon-72x72.png -------------------------------------------------------------------------------- /src/assets/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/icons/icon-96x96.png -------------------------------------------------------------------------------- /src/assets/img/user-generic.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/img/user-generic.jpeg -------------------------------------------------------------------------------- /src/assets/bookmarks.dev.logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/bookmarks.dev.logo.xcf -------------------------------------------------------------------------------- /src/assets/codingmarks-favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/codingmarks-favicon.ico -------------------------------------------------------------------------------- /src/assets/codingmarks-logo-md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/codingmarks-logo-md.png -------------------------------------------------------------------------------- /src/assets/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/icons/icon-128x128.png -------------------------------------------------------------------------------- /src/assets/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/icons/icon-144x144.png -------------------------------------------------------------------------------- /src/assets/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/icons/icon-152x152.png -------------------------------------------------------------------------------- /src/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /src/assets/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/icons/icon-384x384.png -------------------------------------------------------------------------------- /src/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /src/assets/bookmarks-dev-context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/bookmarks-dev-context.png -------------------------------------------------------------------------------- /src/assets/bookmarks.dev-logo-md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/bookmarks.dev-logo-md.png -------------------------------------------------------------------------------- /src/assets/img/gravatar-default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/img/gravatar-default.jpg -------------------------------------------------------------------------------- /src/assets/img/search-bar-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/img/search-bar-example.png -------------------------------------------------------------------------------- /src/app/personal/update/update-personal-bookmark.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/public/privacy/privacy-policy.component.scss: -------------------------------------------------------------------------------- 1 | .bookmarks-context { 2 | display: flex; 3 | justify-content: center; 4 | margin-top: 2rem; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/public/terms/terms-of-service.component.scss: -------------------------------------------------------------------------------- 1 | .bookmarks-context { 2 | display: flex; 3 | justify-content: center; 4 | margin-top: 2rem; 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/fb-twitter-bookmarks.dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/fb-twitter-bookmarks.dev.png -------------------------------------------------------------------------------- /src/assets/fb-twitter-bookmarks.dev.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/fb-twitter-bookmarks.dev.xcf -------------------------------------------------------------------------------- /src/app/user/dashboard/saved-searches/delete-saved-search-dialog/delete-saved-search-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .app-dialog-actions { 2 | margin-bottom: 0.1rem; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/user/dashboard/tags/delete-bookmarks-by-tag-dialog/delete-bookmarks-by-tag-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .app-dialog-actions { 2 | margin-bottom: 0.1rem; 3 | } 4 | -------------------------------------------------------------------------------- /documentation/graphviz/components-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/documentation/graphviz/components-graph.png -------------------------------------------------------------------------------- /src/app/personal/save-bookmark-form/public-bookmark-present-dialog/public-bookmark-present-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .app-dialog-actions { 2 | margin-bottom: 0.1rem; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/core/model/webpage-info.ts: -------------------------------------------------------------------------------- 1 | export interface WebpageInfo { 2 | title: string; 3 | metaDescription: string; 4 | tags?: string[]; 5 | publishedOn?: Date; 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/img/public-vs-private-bookmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/img/public-vs-private-bookmarks.png -------------------------------------------------------------------------------- /src/app/personal/copy-to-mine/copy-to-mine-bookmark.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/img/public-vs-private-bookmarks-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/src/assets/img/public-vs-private-bookmarks-big.png -------------------------------------------------------------------------------- /src/app/codelet/create/create-codelet.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/core/model/used-tag.ts: -------------------------------------------------------------------------------- 1 | export interface UsedTags { 2 | public: UsedTag[], 3 | private: UsedTag[] 4 | } 5 | export interface UsedTag { 6 | name: string; 7 | count: number; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/personal/create/create-personal-bookmark.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/app/core/model/user-data-profile.ts: -------------------------------------------------------------------------------- 1 | import { Profile } from './user-data'; 2 | 3 | export interface UserDataProfile { 4 | userId?: string 5 | profile: Profile 6 | } 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/app/shared/loader/loader.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | -------------------------------------------------------------------------------- /documentation/chrome-extension/img/tiles-chorme-extension-for-store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/documentation/chrome-extension/img/tiles-chorme-extension-for-store.png -------------------------------------------------------------------------------- /src/app/core/model/user-info.oidc.ts: -------------------------------------------------------------------------------- 1 | export interface UserInfoOidc { 2 | sub: string; 3 | name: string; 4 | preferred_username: string; 5 | given_name: string; 6 | family_name: string; 7 | email: string; 8 | } 9 | -------------------------------------------------------------------------------- /documentation/chrome-extension/img/screenshot-chorme-extension-for-store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/documentation/chrome-extension/img/screenshot-chorme-extension-for-store.png -------------------------------------------------------------------------------- /documentation/chrome-extension/img/screenshot-chrom-extension-explanation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodepediaOrg/bookmarks.dev-frontend-only-archive/HEAD/documentation/chrome-extension/img/screenshot-chrom-extension-explanation.png -------------------------------------------------------------------------------- /src/app/personal/personal-bookmarks.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | template: ` 5 | 6 | ` 7 | }) 8 | export class PersonalBookmarksComponent { } 9 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | // styles applied on :host are applied on the current component, "app" in this case 2 | :host { 3 | display: block; 4 | } 5 | 6 | footer { 7 | margin-top: 1rem; 8 | text-align: center; 9 | font-size: 0.8em; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/not-found.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | template: '

Page not found - please verify the URL

' 5 | }) 6 | export class PageNotFoundComponent {} 7 | -------------------------------------------------------------------------------- /src/app/user/dashboard/tags/user-tags.component.scss: -------------------------------------------------------------------------------- 1 | .used-tag { 2 | font-size: 1.3rem; 3 | margin-top: 1rem; 4 | margin-right: 1.3rem; 5 | white-space:nowrap; 6 | a { 7 | padding: 0.3rem; 8 | } 9 | .tag-count { 10 | font-size: 1rem; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /documentation/graphviz/how-to-generate-png.md: -------------------------------------------------------------------------------- 1 | ## Install graphviz 2 | 3 | On Mac: 4 | ```bash 5 | brew install graphviz 6 | ``` 7 | 8 | ## Generate picture 9 | 10 | ```bash 11 | cd documentation/graphviz 12 | 13 | dot -Tpng components-graph.gv -o components-graph.png 14 | ``` 15 | -------------------------------------------------------------------------------- /src/app/core/logger.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class Logger { 5 | logs: string[] = []; // capture logs for testing 6 | log(message: string) { 7 | this.logs.push(message); 8 | console.log(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/app/core/model/user-public-data.ts: -------------------------------------------------------------------------------- 1 | import { Profile } from './user-data'; 2 | import { UsedTag } from './used-tag'; 3 | 4 | export interface UserPublicData { 5 | userId: string, 6 | publicProfile?: Profile, 7 | followers?: string[], 8 | topUsedPublicTags?: UsedTag[] 9 | } 10 | -------------------------------------------------------------------------------- /src/app/user/dashboard/saved-searches/saved-searches.component.scss: -------------------------------------------------------------------------------- 1 | .my-saved-search { 2 | font-size: 1.3rem; 3 | margin-top: 1rem; 4 | margin-right: 1.3rem; 5 | white-space:nowrap; 6 | a { 7 | padding: 0.3rem; 8 | } 9 | .tag-count { 10 | font-size: 1rem; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/codelet/copy-snippet-button/copy-snippet-button.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/app/codelet/create/create-codelet.component.scss: -------------------------------------------------------------------------------- 1 | #tags input.mat-chip-input { 2 | text-indent: 10px; 3 | 4 | } 5 | #tags input.ng-valid { 6 | border-left: 0; 7 | } 8 | 9 | .markdown-link { 10 | margin-left: 0.15rem; 11 | } 12 | 13 | .form-group-distanced { 14 | margin-bottom: 1.5rem; 15 | } 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://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 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/app/core/model/rate-bookmark.request.ts: -------------------------------------------------------------------------------- 1 | import {Bookmark} from './bookmark'; 2 | 3 | export interface RateBookmarkRequest { 4 | ratingUserId: string; 5 | action: RatingActionType; 6 | bookmark: Bookmark 7 | } 8 | 9 | export enum RatingActionType { 10 | LIKE = 'LIKE', 11 | UNLIKE = 'UNLIKE' 12 | } 13 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "", 6 | "types": ["node"], 7 | "typeRoots": [ "../node_modules/@types" ] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/app/personal/create/create-personal-bookmark.component.scss: -------------------------------------------------------------------------------- 1 | #tags input.mat-chip-input { 2 | text-indent: 10px; 3 | 4 | } 5 | #tags input.ng-valid { 6 | border-left: 0; 7 | } 8 | 9 | .markdown-link { 10 | margin-left: 0.15rem; 11 | } 12 | 13 | .form-group-distanced { 14 | margin-bottom: 1.5rem; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | 4 | import 'styles.scss'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.scss'], 10 | }) 11 | export class AppComponent { 12 | url = 'https://www.bookmarks.dev'; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/shared/play-youtube-video-dialog/play-youtube-video-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .videoWrapper { 2 | position: relative; 3 | padding-bottom: 56.25%; /* 16:9 */ 4 | padding-top: 25px; 5 | height: 0; 6 | } 7 | .videoWrapper iframe { 8 | position: absolute; 9 | top: 0; 10 | left: 0; 11 | width: 100%; 12 | height: 100%; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/core/loader/loader.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Subject} from 'rxjs'; 3 | 4 | @Injectable() 5 | export class LoaderService { 6 | 7 | isLoading = new Subject(); 8 | 9 | show() { 10 | this.isLoading.next(true); 11 | } 12 | 13 | hide() { 14 | this.isLoading.next(false); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/core/model/tags.ts: -------------------------------------------------------------------------------- 1 | import {Bookmark} from './bookmark'; 2 | 3 | export class Tag { 4 | name: string; 5 | bookmarks: Bookmark[]; 6 | userId: String; 7 | shared: boolean; 8 | 9 | constructor ( 10 | name: string, 11 | bookmarks: Bookmark[] 12 | ) { 13 | this.name = name; 14 | this.bookmarks = bookmarks; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/shared/loader/loader.component.css: -------------------------------------------------------------------------------- 1 | .overlay { 2 | position: fixed; 3 | display: block; 4 | width: 100%; 5 | height: 100%; 6 | top: 0; 7 | left: 0; 8 | background-color: rgba(74, 74, 74, 0.15); 9 | z-index: 99999; 10 | } 11 | 12 | .spinner { 13 | position: absolute; 14 | top: 50%; 15 | left: 50%; 16 | transform: translate(-50%, -50%); 17 | } 18 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "test.ts", 13 | "polyfills.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/app/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class AppService { 8 | 9 | public logoClicked = new Subject(); 10 | 11 | constructor() { 12 | } 13 | 14 | clickLogo(logoClicked: boolean) { 15 | this.logoClicked.next(logoClicked); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/codelet/save-codelet-form/save-codelet-form.component.scss: -------------------------------------------------------------------------------- 1 | #tags input.mat-chip-input { 2 | text-indent: 10px; 3 | 4 | } 5 | #tags input.ng-valid { 6 | border-left: 0; 7 | } 8 | 9 | .markdown-link { 10 | margin-left: 0.15rem; 11 | } 12 | 13 | .form-group-distanced { 14 | margin-bottom: 1.5rem; 15 | } 16 | 17 | .level_icon { 18 | margin-left: 0.4rem; 19 | } 20 | -------------------------------------------------------------------------------- /src/app/personal/save-bookmark-form/save-bookmark-form.component.scss: -------------------------------------------------------------------------------- 1 | #tags input.mat-chip-input { 2 | text-indent: 10px; 3 | 4 | } 5 | #tags input.ng-valid { 6 | border-left: 0; 7 | } 8 | 9 | .markdown-link { 10 | margin-left: 0.15rem; 11 | } 12 | 13 | .form-group-distanced { 14 | margin-bottom: 1.5rem; 15 | } 16 | 17 | .level_icon { 18 | margin-left: 0.4rem; 19 | } 20 | -------------------------------------------------------------------------------- /src/app/core/error/error.service.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "@angular/core"; 2 | 3 | import { Error } from "./error.model"; 4 | 5 | export class ErrorService { 6 | errorOccurred = new EventEmitter(); 7 | 8 | handleError(error: any) { 9 | const errorData = new Error(error.title, error.messages); 10 | this.errorOccurred.emit(errorData); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/personal/markdown.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | 3 | import * as showdown from 'showdown'; 4 | 5 | // const showdown = require('showdown'); 6 | const converter = new showdown.Converter(); 7 | 8 | @Injectable() 9 | export class MarkdownService { 10 | // converter object is not typescript 11 | 12 | toHtml(text: string) { 13 | return converter.makeHtml(text); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /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.log(err)); 13 | -------------------------------------------------------------------------------- /src/app/social-buttons/social-buttons.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { TweetComponent } from './tweet.component'; 3 | import { FbShareComponent } from './fb-share.component'; 4 | 5 | @NgModule({ 6 | declarations: [ 7 | TweetComponent, 8 | FbShareComponent 9 | ], 10 | exports: [ 11 | TweetComponent, 12 | FbShareComponent 13 | ] 14 | }) 15 | export class SocialButtonsModule {} 16 | -------------------------------------------------------------------------------- /src/app/public/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { environment } from '../../../environments/environment'; 3 | 4 | @Component({ 5 | selector: 'app-about', 6 | templateUrl: './about.component.html', 7 | styleUrls: ['./about.component.scss'] 8 | }) 9 | export class AboutComponent implements OnInit { 10 | 11 | environment = environment; 12 | 13 | ngOnInit() {} 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/public/howto/howto.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { environment } from '../../../environments/environment'; 3 | 4 | @Component({ 5 | selector: 'app-howto', 6 | templateUrl: './howto.component.html', 7 | styleUrls: ['./howto.component.scss'] 8 | }) 9 | export class HowtoComponent implements OnInit { 10 | 11 | environment = environment; 12 | 13 | ngOnInit() {} 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/shared/play-youtube-video-dialog/play-youtube-video-dialog.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 | 7 |
8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/app/user-settings/user-profile/user-profile.component.scss: -------------------------------------------------------------------------------- 1 | .my-drop-zone { border: dotted 3px lightgray; } 2 | .nv-file-over { border: dotted 3px red; } /* Default class applied to drop zones on over */ 3 | .another-file-over-class { border: dotted 3px green; } 4 | 5 | .profile-image { 6 | display: flex; 7 | align-items: center; 8 | .generic-profile-image, img { 9 | margin-right: 2rem; 10 | font-size: 8rem; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/core/navigation/navigation.component.scss: -------------------------------------------------------------------------------- 1 | .add-new-bookmark { 2 | margin-top: 0.3rem; 3 | } 4 | 5 | .navbar-brand { 6 | line-height:12px; 7 | padding-top:10px; 8 | 9 | font-size: 25px; 10 | } 11 | 12 | .navbar-brand small { 13 | font-size:11px; 14 | } 15 | 16 | .app-brand { 17 | display: flex; 18 | padding-right: 15px; 19 | align-items: center; 20 | 21 | .app-brand-text { 22 | margin-left: 0.5rem; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/app/public/bookmarklets/bookmarklet.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { environment } from '../../../environments/environment'; 3 | 4 | @Component({ 5 | selector: 'app-about', 6 | templateUrl: './bookmarklet.component.html', 7 | styleUrls: ['./bookmarklet.component.scss'] 8 | }) 9 | export class BookmarkletComponent implements OnInit { 10 | 11 | environment = environment; 12 | 13 | ngOnInit() {} 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | APP_HOME_URL: 'http://localhost:4200', 4 | API_URL: 'http://localhost:3000/api', 5 | HOST: 'http://localhost:4200', 6 | keycloak: { 7 | 'realm': 'bookmarks', 8 | 'url': 'http://localhost:8480/auth', 9 | 'clientId': 'bookmarks' 10 | }, 11 | PAGINATION_PAGE_SIZE: 5, 12 | TOP_PUBLIC_USER_TAGS_LIMIT: 10, 13 | RECENT_PUBLIC_USER_BOOKMARKS_LIMIT: 5 14 | }; 15 | -------------------------------------------------------------------------------- /src/app/core/loader/loader.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { LoaderService } from './loader.service'; 4 | 5 | describe('LoaderService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [LoaderService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([LoaderService], (service: LoaderService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/environments/environment.non-docker.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | APP_HOME_URL: 'http://localhost:4200', 4 | API_URL: 'http://localhost:3000/api', 5 | HOST: 'http://localhost:4200', 6 | keycloak: { 7 | 'realm': 'bookmarks', 8 | 'url': 'http://localhost:8380/auth', 9 | 'clientId': 'bookmarks' 10 | }, 11 | PAGINATION_PAGE_SIZE: 10, 12 | TOP_PUBLIC_USER_TAGS_LIMIT: 10, 13 | RECENT_PUBLIC_USER_BOOKMARKS_LIMIT: 5 14 | }; 15 | -------------------------------------------------------------------------------- /src/app/public/privacy/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.scss'] 7 | }) 8 | export class PrivacyPolicyComponent implements OnInit { 9 | 10 | constructor() { 11 | // Do stuff 12 | } 13 | 14 | ngOnInit() { 15 | console.log('Hello privacy-policy'); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/app/public/terms/terms-of-service.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-terms-of-service', 5 | templateUrl: './terms-of-service.component.html', 6 | styleUrls: ['./terms-of-service.component.scss'] 7 | }) 8 | export class TermsOfServiceComponent implements OnInit { 9 | 10 | constructor() { 11 | // Do stuff 12 | } 13 | 14 | ngOnInit() { 15 | console.log('Hello privacy-policy'); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | APP_HOME_URL: 'https://www.bookmarks.dev', 4 | API_URL: 'https://www.bookmarks.dev/api', 5 | HOST: 'https://www.bookmarks.dev', 6 | keycloak: { 7 | 'realm': 'bookmarks', 8 | 'url': 'https://www.bookmarks.dev/auth', 9 | 'clientId': 'bookmarks' 10 | }, 11 | PAGINATION_PAGE_SIZE: 10, 12 | TOP_PUBLIC_USER_TAGS_LIMIT: 10, 13 | RECENT_PUBLIC_USER_BOOKMARKS_LIMIT: 5 14 | }; 15 | -------------------------------------------------------------------------------- /src/app/core/model/codelet.ts: -------------------------------------------------------------------------------- 1 | export interface Codelet { 2 | _id?: string; 3 | title: string; 4 | codeSnippets: CodeSnippet[]; 5 | tags: string[]; 6 | userId?: string; 7 | sourceUrl: string; // the location the codelet might have been inspired from to follow for further analysis 8 | public?: boolean; 9 | createdAt?: Date; 10 | updatedAt?: Date; 11 | lastAccessedAt?: Date; 12 | likeCount?: number; 13 | } 14 | 15 | export interface CodeSnippet { 16 | code: string; 17 | comment: string; 18 | } 19 | -------------------------------------------------------------------------------- /src/environments/environment.prod-backend.local-testing.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | APP_HOME_URL: 'http://localhost:4200', 4 | API_URL: 'https://www.bookmarks.dev/api', 5 | HOST: 'https://www.bookmarks.dev', 6 | keycloak: { 7 | 'realm': 'bookmarks', 8 | 'url': 'https://www.bookmarks.dev/auth', 9 | 'clientId': 'bookmarks' 10 | }, 11 | PAGINATION_PAGE_SIZE: 10, 12 | TOP_PUBLIC_USER_TAGS_LIMIT: 10, 13 | RECENT_PUBLIC_USER_BOOKMARKS_LIMIT: 5 14 | }; 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/shared/login-required-dialog/login-required-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Login required

2 | 3 |
4 | 5 | 6 |

{{message}}

7 |
8 | 9 |
10 | 11 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/app/core/loader/loader-interceptor.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { LoaderInterceptorService } from './loader-interceptor.service'; 4 | 5 | describe('LoaderInterceptorService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [LoaderInterceptorService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([LoaderInterceptorService], (service: LoaderInterceptorService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/core/user/notify-stores.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { Bookmark } from '../model/bookmark'; 4 | 5 | @Injectable() 6 | export class NotifyStoresService { 7 | 8 | // Observable string sources 9 | private bookmarkDeleteSource = new Subject(); 10 | 11 | // Observable string streams 12 | bookmarkDeleted$ = this.bookmarkDeleteSource.asObservable(); 13 | 14 | deleteBookmark(bookmark: Bookmark) { 15 | this.bookmarkDeleteSource.next(bookmark); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/app/user-settings/user-profile/image-upload.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Injectable() 6 | export class ImageUploadService { 7 | 8 | constructor(private httpClient: HttpClient) {} 9 | 10 | 11 | public uploadImage(image: File): Observable { 12 | const formData = new FormData(); 13 | 14 | formData.append('image', image); 15 | 16 | return this.httpClient.post('/api/v1/image-upload', formData); 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/public/about/about.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AboutComponent } from './about.component'; 4 | 5 | describe('About Component', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({declarations: [AboutComponent]}); 8 | }); 9 | 10 | it('should ...', () => { 11 | const fixture = TestBed.createComponent(AboutComponent); 12 | fixture.detectChanges(); 13 | expect(fixture.nativeElement.children[0].textContent).toContain('Bookmarking for Developers & Co'); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/public/howto/howto.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { HowtoComponent } from './howto.component'; 4 | 5 | describe('About Component', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({declarations: [HowtoComponent]}); 8 | }); 9 | 10 | it('should ...', () => { 11 | const fixture = TestBed.createComponent(HowtoComponent); 12 | fixture.detectChanges(); 13 | expect(fixture.nativeElement.children[0].textContent).toContain('Collection of (cu)rated coding bookmarks'); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/public/privacy/privacy-policy.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { PrivacyComponent } from './howto.component'; 4 | 5 | describe('About Component', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({declarations: [PrivacyComponent]}); 8 | }); 9 | 10 | it('should ...', () => { 11 | const fixture = TestBed.createComponent(PrivacyComponent); 12 | fixture.detectChanges(); 13 | expect(fixture.nativeElement.children[0].textContent).toContain('Collection of (cu)rated coding bookmarks'); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/public/terms/terms-of-service.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { PrivacyComponent } from './howto.component'; 4 | 5 | describe('About Component', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({declarations: [PrivacyComponent]}); 8 | }); 9 | 10 | it('should ...', () => { 11 | const fixture = TestBed.createComponent(PrivacyComponent); 12 | fixture.detectChanges(); 13 | expect(fixture.nativeElement.children[0].textContent).toContain('Collection of (cu)rated coding bookmarks'); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/core/model/bookmark.ts: -------------------------------------------------------------------------------- 1 | export interface Bookmark { 2 | _id?: string; 3 | name: string; 4 | location: string; 5 | description?: string; 6 | descriptionHtml?: string; 7 | tags: string[]; 8 | tagsLine?: string; 9 | publishedOn?: Date; 10 | sourceCodeURL?: string; 11 | userId?: String; 12 | userDisplayName: String; 13 | public?: boolean; 14 | language: string; 15 | createdAt?: Date; 16 | updatedAt?: Date; 17 | lastAccessedAt?: Date; 18 | ownerVisitCount?: number; 19 | likeCount?: number; 20 | youtubeVideoId?: string; 21 | stackoverflowQuestionId?: string; 22 | } 23 | -------------------------------------------------------------------------------- /src/app/public/bookmarklets/bookmarklet.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { BookmarkletComponent } from './bookmarklet.component'; 4 | 5 | describe('About Component', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({declarations: [BookmarkletComponent]}); 8 | }); 9 | 10 | it('should ...', () => { 11 | const fixture = TestBed.createComponent(BookmarkletComponent); 12 | fixture.detectChanges(); 13 | expect(fixture.nativeElement.children[0].textContent).toContain('Bookmarking for Developers & Co'); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/codelet/delete-codelet-dialog/delete-codelet-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Delete Codelet

2 | 3 |
4 | 5 | 6 | Are you sure you want to delete the codelet 7 |

"{{codeletTitle}}" ?

8 | 9 |
10 | 11 |
12 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/app/codelet/update/update-codelet.component.scss: -------------------------------------------------------------------------------- 1 | label { 2 | display: inline-block; 3 | margin: .5em 0; 4 | color: #607D8B; 5 | font-weight: bold; 6 | } 7 | input { 8 | height: 2em; 9 | font-size: 1em; 10 | padding-left: .4em; 11 | } 12 | 13 | input[type=checkbox], input[type=radio] { 14 | vertical-align: middle; 15 | position: relative; 16 | bottom: 1px; 17 | } 18 | 19 | #shared_label { 20 | width:100%; 21 | } 22 | 23 | mat-chip { 24 | max-width: 200px; 25 | } 26 | 27 | .demo-chip-list { 28 | width: 100%; 29 | } 30 | 31 | .markdown-link { 32 | font-weight: normal; 33 | margin-left: 0.15rem; 34 | } 35 | -------------------------------------------------------------------------------- /src/app/user/dashboard/saved-searches/delete-saved-search-dialog/delete-saved-search-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Delete saved search

2 | 3 |
4 | 5 | 6 | Are you sure you want to delete saved search with text - {{savedSearchText}}? 7 | 8 | 9 |
10 | 11 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/app/personal/copy-to-mine/copy-to-min-bookmark.component.scss: -------------------------------------------------------------------------------- 1 | label { 2 | display: inline-block; 3 | margin: .5em 0; 4 | color: #607D8B; 5 | font-weight: bold; 6 | } 7 | input { 8 | height: 2em; 9 | font-size: 1em; 10 | padding-left: .4em; 11 | } 12 | 13 | input[type=checkbox], input[type=radio] { 14 | vertical-align: middle; 15 | position: relative; 16 | bottom: 1px; 17 | } 18 | 19 | #shared_label { 20 | width:100%; 21 | } 22 | 23 | mat-chip { 24 | max-width: 200px; 25 | } 26 | 27 | .demo-chip-list { 28 | width: 100%; 29 | } 30 | 31 | .markdown-link { 32 | font-weight: normal; 33 | margin-left: 0.15rem; 34 | } 35 | -------------------------------------------------------------------------------- /src/app/personal/update/update-personal-bookmark.component.scss: -------------------------------------------------------------------------------- 1 | label { 2 | display: inline-block; 3 | margin: .5em 0; 4 | color: #607D8B; 5 | font-weight: bold; 6 | } 7 | input { 8 | height: 2em; 9 | font-size: 1em; 10 | padding-left: .4em; 11 | } 12 | 13 | input[type=checkbox], input[type=radio] { 14 | vertical-align: middle; 15 | position: relative; 16 | bottom: 1px; 17 | } 18 | 19 | #shared_label { 20 | width:100%; 21 | } 22 | 23 | mat-chip { 24 | max-width: 200px; 25 | } 26 | 27 | .demo-chip-list { 28 | width: 100%; 29 | } 30 | 31 | .markdown-link { 32 | font-weight: normal; 33 | margin-left: 0.15rem; 34 | } 35 | -------------------------------------------------------------------------------- /src/app/shared/bookmark-text.component.scss: -------------------------------------------------------------------------------- 1 | .less_text { 2 | height: 90px; 3 | overflow: hidden; 4 | } 5 | 6 | .more_text { 7 | overflow: visible; 8 | } 9 | 10 | .show_less_button:after{ 11 | font-family: "Font Awesome 5 Free"; 12 | content: "SHOW LESS \f0aa"; 13 | } 14 | .show_more_button:after{ 15 | font-family: "Font Awesome 5 Free"; 16 | content: "SHOW MORE \f0ab"; 17 | } 18 | 19 | .toggle-show-more-button { 20 | outline: none; 21 | opacity: 0.55; 22 | margin-top: 0.8rem; 23 | background-color: white; 24 | border: none; 25 | font-weight: bolder; 26 | font-size: smaller; 27 | box-shadow: 0px 2px 3px 0px #ccc; 28 | } 29 | -------------------------------------------------------------------------------- /src/app/shared/bookmark-text.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /src/app/user/dashboard/tags/delete-bookmarks-by-tag-dialog/delete-bookmarks-by-tag-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Delete private bookmarks tagged [{{tag}}]

2 | 3 |
4 | 5 | 6 | Are you sure you want to delete ALL private bookmarks tagged with [{{tag}}]? 7 | 8 | 9 |
10 | 11 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /documentation/graphviz/components-graph.gv: -------------------------------------------------------------------------------- 1 | digraph technical_context_diagram { 2 | rankdir=LR; //Rank Direction Left to Right 3 | user [label="User"] 4 | angular [label="Front-end\n(Angular)", style=filled, fillcolor=green, shape=rectangle] 5 | api [label="Api\n(Node.js)", shape=rectangle, style=filled, fillcolor=yellow] 6 | keycloak [label="Auth\n(Keycloak)", shape=rectangle] 7 | postgres [label="Postgres DB", shape="cylinder"] 8 | mongo [label="Data store\n(Mongo DB)", shape="cylinder"] 9 | 10 | user -> angular; 11 | angular -> api; 12 | api -> mongo; 13 | angular -> keycloak; 14 | api -> keycloak; 15 | keycloak -> postgres; 16 | } 17 | -------------------------------------------------------------------------------- /src/app/core/pagination-notification.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, Subject } from 'rxjs'; 3 | import { PaginationAction } from './model/pagination-action'; 4 | 5 | @Injectable() 6 | export class PaginationNotificationService { 7 | 8 | // Observable string sources 9 | private pageNavigationClickedSource = new Subject(); 10 | 11 | // Observable string streams 12 | pageNavigationClicked$: Observable = this.pageNavigationClickedSource.asObservable(); 13 | 14 | clickPageNavigation(paginationAction: PaginationAction) { 15 | this.pageNavigationClickedSource.next(paginationAction); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/app/public/search/bookmark-search.component.scss: -------------------------------------------------------------------------------- 1 | .search-result{ 2 | border-bottom: 1px solid gray; 3 | border-left: 1px solid gray; 4 | border-right: 1px solid gray; 5 | width:195px; 6 | height: 20px; 7 | padding: 5px; 8 | background-color: white; 9 | cursor: pointer; 10 | } 11 | 12 | .search-control input:focus { 13 | outline-width: 0; 14 | } 15 | 16 | .search-component{ 17 | input:focus { 18 | box-shadow: none; 19 | } 20 | } 21 | 22 | .focused { 23 | background-color:#007bff; 24 | border-color: #007bff; 25 | color: white; 26 | cursor: pointer; 27 | } 28 | 29 | .search-domain-select { 30 | float: right; 31 | margin-top: 0.5rem; 32 | } 33 | -------------------------------------------------------------------------------- /src/app/public/user-public-profile/user-public-profile.component.scss: -------------------------------------------------------------------------------- 1 | .view-more-results { 2 | font-size: larger; 3 | } 4 | .public-profile-header { 5 | margin-top: 1rem; 6 | display: flex; 7 | img { 8 | border-radius: 8px; 9 | width: 20%; 10 | max-width: 200px; 11 | } 12 | .summary { 13 | font-size: larger; 14 | } 15 | .social { 16 | a { 17 | font-size: 1.5rem; 18 | padding-right: 0.8rem; 19 | } 20 | } 21 | } 22 | 23 | .public-user-info { 24 | margin-left: 2rem; 25 | } 26 | 27 | .new-tag { 28 | margin-right: 0.5rem; 29 | margin-top: 0.8rem; 30 | a { 31 | font-weight: 600; 32 | padding: 0.25rem 0.1rem 0.25rem 0.5rem; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /.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 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | testem.log 34 | /typings 35 | 36 | # e2e 37 | /e2e/*.js 38 | /e2e/*.map 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | /dist.zip 44 | -------------------------------------------------------------------------------- /src/app/personal/save-bookmark-form/public-bookmark-present-dialog/public-bookmark-present-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Bookmark already public

2 | 3 |
4 | 5 | 6 |

This URL is already public on Bookmarks.dev. You can still save it as private and give it a thumbs up to promote it

7 |
8 | 9 |
10 | 11 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/app/public/tag/tag.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TagComponent } from './tag.component'; 4 | 5 | xdescribe('TagComponent', () => { 6 | let component: TagComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TagComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TagComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/codelet/async-codelet-list/codelet-code-snippets/codelet-card-body.component.scss: -------------------------------------------------------------------------------- 1 | .less_text { 2 | height: 300px; 3 | overflow: hidden; 4 | } 5 | 6 | .more_text { 7 | overflow: visible; 8 | } 9 | 10 | .show_less_button:after{ 11 | font-family: "Font Awesome 5 Free"; 12 | content: "SHOW LESS \f0aa"; 13 | } 14 | .show_more_button:after{ 15 | font-family: "Font Awesome 5 Free"; 16 | content: "SHOW MORE \f0ab"; 17 | } 18 | 19 | .toggle-show-more-button { 20 | outline: none; 21 | opacity: 0.55; 22 | margin-top: 0.8rem; 23 | background-color: white; 24 | border: none; 25 | font-weight: bolder; 26 | font-size: smaller; 27 | box-shadow: 0px 2px 3px 0px #ccc; 28 | } 29 | 30 | .btn-copy-snippet { 31 | margin-bottom: -0.7rem; 32 | } 33 | -------------------------------------------------------------------------------- /ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "/index.html", 3 | "assetGroups": [{ 4 | "name": "app", 5 | "installMode": "prefetch", 6 | "resources": { 7 | "files": [ 8 | "/favicon.ico", 9 | "/index.html", 10 | "/*.css", 11 | "/*.js" 12 | ] 13 | } 14 | }, { 15 | "name": "assets", 16 | "installMode": "lazy", 17 | "updateMode": "prefetch", 18 | "resources": { 19 | "files": [ 20 | "/assets/**" 21 | ] 22 | } 23 | }], 24 | "dataGroups": [ 25 | { 26 | "name": "api-performance", 27 | "urls": [ 28 | "/api" 29 | ], 30 | "cacheConfig": { 31 | "strategy": "freshness", 32 | "maxSize": 100, 33 | "maxAge": "3d" 34 | } 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /src/app/user/dashboard/followers/followers.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { UserDataProfile } from '../../../core/model/user-data-profile'; 4 | import { UserDataService } from '../../../core/user-data.service'; 5 | 6 | @Component({ 7 | selector: 'app-followers', 8 | templateUrl: './followers.component.html', 9 | styleUrls: ['./followers.component.scss'] 10 | }) 11 | export class FollowersComponent implements OnInit { 12 | 13 | @Input() 14 | userId: string; 15 | 16 | followers$: Observable; 17 | 18 | constructor(private userDataService: UserDataService) { } 19 | 20 | ngOnInit() { 21 | this.followers$ = this.userDataService.getFollowers$(this.userId); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/app/user/dashboard/tags/user-tags.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserTagsComponent } from './user-tags.component'; 4 | 5 | describe('UserTagsComponent', () => { 6 | let component: UserTagsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ UserTagsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UserTagsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/user/dashboard/followers/followers.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FollowersComponent } from './followers.component'; 4 | 5 | describe('FollowersComponent', () => { 6 | let component: FollowersComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FollowersComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FollowersComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/user/dashboard/following/following.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FollowingComponent } from './following.component'; 4 | 5 | describe('FollowingComponent', () => { 6 | let component: FollowingComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FollowingComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FollowingComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/user/dashboard/following/following.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | import { UserDataService } from '../../../core/user-data.service'; 3 | import { UserDataProfile } from '../../../core/model/user-data-profile'; 4 | import { Observable } from 'rxjs'; 5 | 6 | @Component({ 7 | selector: 'app-following', 8 | templateUrl: './following.component.html', 9 | styleUrls: ['./following.component.scss'] 10 | }) 11 | export class FollowingComponent implements OnInit { 12 | 13 | @Input() 14 | userId: string; 15 | 16 | followedUsers$: Observable; 17 | 18 | constructor(private userDataService: UserDataService) { } 19 | 20 | ngOnInit() { 21 | this.followedUsers$ = this.userDataService.getFollowedUsers$(this.userId); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/app/core/validators/text-size.validator.ts: -------------------------------------------------------------------------------- 1 | import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; 2 | 3 | export function textSizeValidator(maxNumberOfCharacters: number, maxNumberOfLines: number): ValidatorFn { 4 | 5 | return (control: AbstractControl): ValidationErrors | null => { 6 | 7 | if (!control.value) { 8 | return null; 9 | } 10 | 11 | const numberOfLines = control.value.split('\n').length; 12 | const numberOfCharacters = control.value.length; 13 | const validationResponse = numberOfLines > maxNumberOfLines ? {'tooManyLines': {value: numberOfLines}} 14 | : numberOfCharacters > maxNumberOfCharacters ? {'tooManyCharacters': {value: numberOfCharacters}} : null; 15 | return validationResponse; 16 | } 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/app/shared/highlight.no-html-tags.pipe.ts: -------------------------------------------------------------------------------- 1 | import {Pipe} from '@angular/core'; 2 | import {PipeTransform} from '@angular/core'; 3 | 4 | @Pipe({ name: 'highlightHtml' }) 5 | export class HighLightHtmlPipe implements PipeTransform { 6 | 7 | transform(text: string, search): string { 8 | if (!search || search === undefined) { 9 | return text; 10 | } else { 11 | let pattern = search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); 12 | pattern = pattern.split(' ').filter((t) => { 13 | return t.length > 0; 14 | }).join('|'); 15 | pattern = '(' + pattern + ')' + '(?![^<]*>)'; 16 | const regex = new RegExp(pattern, 'gi'); 17 | 18 | return search ? text.replace(regex, (match) => `${match}`) : text; 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/app/user-settings/user-settings.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserSettingsComponent } from './user-settings.component'; 4 | 5 | describe('UserSettingsComponent', () => { 6 | let component: UserSettingsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ UserSettingsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UserSettingsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/user/dashboard/user-dashboard.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserDashboardComponent } from './user-dashboard.component'; 4 | 5 | describe('DashboardComponent', () => { 6 | let component: UserDashboardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ UserDashboardComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UserDashboardComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/shared/async-bookmark-list.component.scss: -------------------------------------------------------------------------------- 1 | 2 | .title-content-separator { 3 | margin: 0 0 0.5rem 0; 4 | } 5 | 6 | .header-wrapper { 7 | display: flex; 8 | justify-content: space-between; 9 | 10 | .clock-wrapper { 11 | font-size: 1.4rem; 12 | margin-left: 1rem; 13 | cursor: pointer; 14 | } 15 | 16 | .pinned { 17 | color: red; 18 | } 19 | } 20 | 21 | .share-action { 22 | cursor: pointer; 23 | margin-top: 0.3rem; 24 | } 25 | 26 | .external-link-hint { 27 | font-size: 0.6rem; 28 | margin-left: 0.2rem; 29 | } 30 | 31 | .youtube-icon { 32 | color:red; 33 | margin-right: 0.5rem; 34 | cursor: pointer; 35 | } 36 | 37 | .stackoverflow-icon { 38 | margin-right: 0.5rem; 39 | } 40 | 41 | .isDisabled { 42 | cursor: not-allowed; 43 | opacity: 0.6; 44 | pointer-events: none; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/app/shared/social-share-dialog/social-share-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Share Bookmark

2 | 3 |
4 | 5 | 6 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/app/user-settings/user-profile/user-profile.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserProfileComponent } from './user-profile.component'; 4 | 5 | describe('UserPublicProfileEditComponent', () => { 6 | let component: UserProfileComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ UserProfileComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UserProfileComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/core/keycloak-service-wrapper.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 3 | import { environment } from '../../environments/environment'; 4 | import { Router, RouterStateSnapshot } from '@angular/router'; 5 | import { KeycloakService } from 'keycloak-angular'; 6 | 7 | @Injectable() 8 | export class KeycloakServiceWrapper { 9 | 10 | 11 | constructor(private router: Router, private keycloakService: KeycloakService) { 12 | } 13 | 14 | public login() { 15 | const routerStateSnapshot: RouterStateSnapshot = this.router.routerState.snapshot; 16 | const options: Keycloak.KeycloakLoginOptions = {}; 17 | options.redirectUri = environment.APP_HOME_URL + routerStateSnapshot.url; 18 | this.keycloakService.login(options); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/app/user/dashboard/saved-searches/saved-searches.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SavedSearchesComponent } from './saved-searches.component'; 4 | 5 | describe('UserTagsComponent', () => { 6 | let component: SavedSearchesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SavedSearchesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SavedSearchesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/codelet/codelet-details/codelet-details.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CodeletDetailsComponent } from './codelet-details.component'; 4 | 5 | describe('CodeletDetailsComponent', () => { 6 | let component: CodeletDetailsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CodeletDetailsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CodeletDetailsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/user/dashboard/user-bookmarks/user-bookmarks.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserBookmarksComponent } from './user-bookmarks.component'; 4 | 5 | describe('UserBookmarksComponent', () => { 6 | let component: UserBookmarksComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ UserBookmarksComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UserBookmarksComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/codelet/async-codelet-list/async-codelet-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AsyncCodeletListComponent } from './async-codelet-list.component'; 4 | 5 | describe('AsyncCodeletListComponent', () => { 6 | let component: AsyncCodeletListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ AsyncCodeletListComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AsyncCodeletListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/app.component.e2e-spec.js: -------------------------------------------------------------------------------- 1 | describe('App', function () { 2 | 3 | beforeEach(function () { 4 | browser.get('/'); 5 | }); 6 | 7 | it('should have a title', function () { 8 | expect(browser.getTitle()).toEqual("Angular 2 App | ng2-webpack"); 9 | }); 10 | 11 | it('should have
', function () { 12 | expect(element(by.css('my-app header')).isPresent()).toEqual(true); 13 | }); 14 | 15 | it('should have
', function () { 16 | expect(element(by.css('my-app main')).isPresent()).toEqual(true); 17 | }); 18 | 19 | it('should have a main title', function () { 20 | expect(element(by.css('main h1')).getText()).toEqual('Hello from Angular 2!'); 21 | }); 22 | 23 | it('should have