Skip to content

Commit c695ca3

Browse files
committed
Merge branch 'feat/add-toc-to-note-details'
2 parents 9f765bb + b9cadc1 commit c695ca3

44 files changed

Lines changed: 1636 additions & 493 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/codever-api/src/routes/users/notes/personal-notes.service.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const Note = require('../../../model/note');
2+
const User = require('../../../model/user');
23

34
const NotFoundError = require('../../../error/not-found.error');
45

@@ -92,6 +93,17 @@ let deleteNoteById = async (userId, noteId) => {
9293
if (!note) {
9394
throw new NotFoundError('Note NOT_FOUND with id: ' + noteId);
9495
}
96+
97+
// Remove the note from users' pinned and history lists, in case it was there
98+
await User.updateMany(
99+
{},
100+
{
101+
$pull: {
102+
pinned: noteId,
103+
history: noteId,
104+
},
105+
}
106+
);
95107
};
96108

97109
/* GET suggested tags used for user */

apps/codever-api/src/routes/users/user-data.service.js

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const constants = require('../../common/constants');
22

33
const User = require('../../model/user');
44
const Bookmark = require('../../model/bookmark');
5+
const Note = require('../../model/note');
56

67
const ValidationError = require('../../error/validation.error');
78
const NotFoundError = require('../../error/not-found.error');
@@ -347,7 +348,7 @@ let getUsedTagsForPrivateBookmarks = async function (userId) {
347348
return usedTags;
348349
};
349350

350-
let getPinnedBookmarks = async function (userId, page, limit) {
351+
let getPinnedResources = async function (userId, page, limit) {
351352
const userData = await User.findOne({
352353
userId: userId,
353354
});
@@ -358,14 +359,22 @@ let getPinnedBookmarks = async function (userId, page, limit) {
358359
(page - 1) * limit,
359360
(page - 1) * limit + limit
360361
);
361-
const bookmarks = await Bookmark.find({ _id: { $in: pinnedRangeIds } });
362-
//we need to order the bookmarks to correspond the one in the userData.pinned array
363-
const orderedBookmarksAsInPinned = bookmarks.sort(function (a, b) {
364-
return pinnedRangeIds.indexOf(a._id) - pinnedRangeIds.indexOf(b._id);
362+
// Pinned entries can be either bookmarks or notes; look both collections up
363+
const [bookmarks, notes] = await Promise.all([
364+
Bookmark.find({ _id: { $in: pinnedRangeIds } }),
365+
Note.find({ _id: { $in: pinnedRangeIds } }),
366+
]);
367+
const pinnedResources = [...bookmarks, ...notes];
368+
//we need to order the resources to correspond the one in the userData.pinned array
369+
const orderedResourcesAsInPinned = pinnedResources.sort(function (a, b) {
370+
return (
371+
pinnedRangeIds.indexOf(a._id.toString()) -
372+
pinnedRangeIds.indexOf(b._id.toString())
373+
);
365374
});
366375

367-
return orderedBookmarksAsInPinned.filter(
368-
(bookmark) => bookmark !== undefined
376+
return orderedResourcesAsInPinned.filter(
377+
(resource) => resource !== undefined
369378
);
370379
}
371380
};
@@ -408,16 +417,22 @@ let getBookmarksFromHistory = async function (userId, page, limit) {
408417
(page - 1) * limit,
409418
(page - 1) * limit + limit
410419
);
411-
const bookmarks = await Bookmark.find({ _id: { $in: historyRangeIds } });
420+
// History entries can be either bookmarks or notes; look both collections up
421+
const [bookmarks, notes] = await Promise.all([
422+
Bookmark.find({ _id: { $in: historyRangeIds } }),
423+
Note.find({ _id: { $in: historyRangeIds } }),
424+
]);
425+
const historyResources = [...bookmarks, ...notes];
412426

413-
//we need to order the bookmarks to correspond the one in the userData.history array
414-
const orderedBookmarksAsInHistory = bookmarks.sort(function (a, b) {
415-
return historyRangeIds.indexOf(a._id) - historyRangeIds.indexOf(b._id);
427+
//we need to order the resources to correspond the one in the userData.history array
428+
const orderedResourcesAsInHistory = historyResources.sort(function (a, b) {
429+
return (
430+
historyRangeIds.indexOf(a._id.toString()) -
431+
historyRangeIds.indexOf(b._id.toString())
432+
);
416433
});
417434

418-
//check for "potentially" deleted bookmarks via "delete all private for tag"
419-
//return orderedBookmarksAsInHistory.filter(bookmark => bookmark !== undefined);
420-
return orderedBookmarksAsInHistory;
435+
return orderedResourcesAsInHistory;
421436
}
422437
};
423438

@@ -428,16 +443,25 @@ let getAllBookmarksFromHistory = async function (userId) {
428443
if (!userData) {
429444
throw new NotFoundError(`User data NOT_FOUND for userId: ${userId}`);
430445
} else {
431-
const bookmarks = await Bookmark.find({ _id: { $in: userData.history } });
446+
// History entries can be either bookmarks or notes; look both collections up
447+
const [bookmarks, notes] = await Promise.all([
448+
Bookmark.find({ _id: { $in: userData.history } }),
449+
Note.find({ _id: { $in: userData.history } }),
450+
]);
451+
const historyResources = [...bookmarks, ...notes];
432452

433-
//we need to order the bookmarks to correspond the one in the userData.history array
434-
const allBookmarksOrderedFromHistory = bookmarks.sort(function (a, b) {
435-
return userData.history.indexOf(a._id) - userData.history.indexOf(b._id);
453+
//we need to order the resources to correspond the one in the userData.history array
454+
const allResourcesOrderedFromHistory = historyResources.sort(function (
455+
a,
456+
b
457+
) {
458+
return (
459+
userData.history.indexOf(a._id.toString()) -
460+
userData.history.indexOf(b._id.toString())
461+
);
436462
});
437463

438-
//check for "potentially" deleted bookmarks via "delete all private for tag"
439-
//return orderedBookmarksAsInHistory.filter(bookmark => bookmark !== undefined);
440-
return allBookmarksOrderedFromHistory;
464+
return allResourcesOrderedFromHistory;
441465
}
442466
};
443467

@@ -695,7 +719,7 @@ module.exports = {
695719
getLikedBookmarks: getLikedBookmarks,
696720
getUsedTagsForPublicBookmarks: getUsedTagsForPublicBookmarks,
697721
getUsedTagsForPrivateBookmarks: getUsedTagsForPrivateBookmarks,
698-
getPinnedBookmarks: getPinnedBookmarks,
722+
getPinnedResources: getPinnedResources,
699723
getFavoriteBookmarks: getFavoriteBookmarks,
700724
getBookmarksFromHistory: getBookmarksFromHistory,
701725
getAllBookmarksFromHistory: getAllBookmarksFromHistory,

apps/codever-api/src/routes/users/user.router.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,13 @@ usersRouter.get(
193193
userIdTokenValidator.validateUserId(request);
194194
const { page, limit } =
195195
PaginationQueryParamsHelper.getPageAndLimit(request);
196-
const pinnedBookmarks = await UserDataService.getPinnedBookmarks(
196+
const pinnedResources = await UserDataService.getPinnedResources(
197197
request.params.userId,
198198
page,
199199
limit
200200
);
201201

202-
response.send(pinnedBookmarks);
202+
response.send(pinnedResources);
203203
}
204204
);
205205

apps/codever-ui/src/app/app.component.html

Lines changed: 6 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -44,94 +44,16 @@
4444
>
4545
<i class="fas fa-sticky-note mr-1"></i> Notes (snippets)
4646
</a>
47-
<app-quick-access-bookmarks
48-
[quickAccessBookmarks]="latestPinnedBookmarks$ | async"
47+
<app-quick-access-resources
48+
[quickAccessResources]="latestPinnedResources$ | async"
4949
[source]="'pinned'"
5050
(newSectionTitleEvent)="launchDialogFromQuickAccess($event)">
51-
</app-quick-access-bookmarks>
52-
<app-quick-access-bookmarks
53-
[quickAccessBookmarks]="latestVisitedBookmarks$ | async"
51+
</app-quick-access-resources>
52+
<app-quick-access-resources
53+
[quickAccessResources]="latestVisitedResources$ | async"
5454
[source]="'last_visited'"
5555
(newSectionTitleEvent)="launchDialogFromQuickAccess($event)">
56-
</app-quick-access-bookmarks>
57-
58-
<ng-container *ngIf="latestSearches$ | async as latestSearches">
59-
<a
60-
class="ml-2 mt-3 bg-light"
61-
title="Ctrl+S for quick access"
62-
(click)="displaySearchBarSearches()"
63-
>
64-
<i class="fas fa-history mr-1"></i> Latest searches (<b>Ctrl</b>+<b>s</b>)
65-
</a>
66-
<div class="mt-1" (mouseout)="resetHoveringLastSearches()">
67-
<ng-container
68-
*ngFor="let mySearch of latestSearches; let i = index"
69-
class="mt-1"
70-
>
71-
<span class="mt-1 mr-2 on-top">
72-
<a
73-
(mouseover)="hoveringLastSearches[i] = true"
74-
(mouseout)="hoveringLastSearches[i] = false"
75-
[routerLink]="['/search']"
76-
[queryParams]="{
77-
q: mySearch.text,
78-
sd: mySearch.searchDomain
79-
}"
80-
class="badge badge-secondary mb-1 ml-2"
81-
[class.my-bookmarks-last-search]="
82-
mySearch.searchDomain === 'my-bookmarks'
83-
"
84-
[class.my-notes-last-search]="
85-
mySearch.searchDomain === 'my-notes'
86-
"
87-
[class.public-notes-last-search]="
88-
mySearch.searchDomain === 'public-notes'
89-
"
90-
[class.public-bookmarks-last-search]="
91-
mySearch.searchDomain === 'public-bookmarks'
92-
"
93-
title="{{
94-
'Search in ' + mySearch.searchDomain + ': ' + mySearch.text
95-
}}"
96-
>
97-
<i class="fa fa-xs fa-search mr-1"></i>
98-
<span *ngIf="!hoveringLastSearches[i]; else longVersion">{{
99-
mySearch.text.length > 30
100-
? mySearch.text.substring(0, 30) + '...'
101-
: mySearch.text
102-
}}</span>
103-
<ng-template #longVersion>{{ mySearch.text }}</ng-template>
104-
<i
105-
*ngIf="
106-
mySearch.searchDomain === 'my-bookmarks' ||
107-
mySearch.searchDomain === 'public-bookmarks'
108-
"
109-
class="fa fa-xs fa-bookmark ml-1"
110-
></i>
111-
<i
112-
*ngIf="
113-
mySearch.searchDomain === 'my-notes' ||
114-
mySearch.searchDomain === 'public-notes'
115-
"
116-
class="fa fa-xs fa-sticky-note ml-1"
117-
></i>
118-
<span *ngIf="mySearch.searchDomain === 'all-mine'">
119-
<i class="fa fa-xs fa-bookmark ml-1"></i>
120-
<i class="fa fa-xs fa-sticky-note ml-1"></i>
121-
</span>
122-
</a>
123-
</span>
124-
</ng-container>
125-
</div>
126-
<a
127-
class="ml-2 mt-1 d-inline-block"
128-
[routerLink]="['/dashboard']"
129-
[queryParams]="{ tab: 'searches' }"
130-
title="See all searches in Dashboard"
131-
>
132-
<i class="fas fa-xs fa-external-link-alt mr-1"></i><small>See more in Dashboard</small>
133-
</a>
134-
</ng-container>
56+
</app-quick-access-resources>
13557
</div>
13658
</div>
13759
<div class="container">

apps/codever-ui/src/app/app.component.ts

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
import { Component, HostListener, OnInit } from '@angular/core';
22

33
import { UserDataHistoryStore } from './core/user/userdata.history.store';
4-
import {
5-
MatDialog,
6-
MatDialogConfig,
7-
} from '@angular/material/dialog';
4+
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
85
import { HotKeysDialogComponent } from './shared/dialog/history-dialog/hot-keys-dialog.component';
96
import { UserDataPinnedStore } from './core/user/userdata.pinned.store';
107
import { UserInfoStore } from './core/user/user-info.store';
118
import { KeycloakService } from 'keycloak-angular';
129
import { LoginRequiredDialogComponent } from './shared/dialog/login-required-dialog/login-required-dialog.component';
1310
import iziToast, { IziToastSettings } from 'izitoast';
1411
import { UserDataStore } from './core/user/userdata.store';
15-
import { Search, UserData } from './core/model/user-data';
12+
import { UserData } from './core/model/user-data';
1613
import { Observable } from 'rxjs';
17-
import { map } from 'rxjs/operators';
18-
import { Bookmark } from './core/model/bookmark';
14+
import { UserDataResource } from './core/model/user-data-resource.type';
1915
import { Router } from '@angular/router';
20-
import { AddToHistoryService } from './core/user/add-to-history.service';
2116
import { environment } from '../environments/environment';
2217
import { ScrollStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay';
2318
import { LoginDialogHelperService } from './core/login-dialog-helper.service';
24-
import { LatestSearchClickNotificationService } from './core/latest-search-click.notification.service';
2519

2620
@Component({
2721
selector: 'app-root',
@@ -37,12 +31,12 @@ export class AppComponent implements OnInit {
3731

3832
userData$: Observable<UserData>;
3933
showWhatsNewNotification = false;
40-
readonly whatsNewNotificationKey = 'whats-new-2026-06-snipptes_2_notes-my_collections-simplified_search';
41-
latestSearches$: Observable<Search[]>;
42-
latestVisitedBookmarks$: Observable<Bookmark[]>;
43-
latestPinnedBookmarks$: Observable<Bookmark[]>;
34+
readonly whatsNewNotificationKey =
35+
'whats-new-2026-06-snipptes_2_notes-my_collections-simplified_search';
36+
latestVisitedResources$: Observable<UserDataResource[]>;
37+
latestPinnedResources$: Observable<UserDataResource[]>;
4438

45-
private hoveringLastSearches: boolean[] = [];
39+
private readonly pinnedQuickAccessLimit = 15;
4640

4741
favIcon: HTMLLinkElement = document.querySelector('#favicon');
4842
readonly environment = environment;
@@ -58,9 +52,7 @@ export class AppComponent implements OnInit {
5852
private historyDialog: MatDialog,
5953
private loginDialog: MatDialog,
6054
private loginDialogHelperService: LoginDialogHelperService,
61-
private latestSearchClickNotificationService: LatestSearchClickNotificationService,
6255
protected router: Router,
63-
private addToHistoryService: AddToHistoryService,
6456
private readonly scrollStrategyOptions: ScrollStrategyOptions
6557
) {
6658
this.innerWidth = 100;
@@ -76,12 +68,16 @@ export class AppComponent implements OnInit {
7668
this.userIsLoggedIn = true;
7769
this.userInfoStore.getUserInfoOidc$().subscribe((userInfo) => {
7870
this.userId = userInfo.sub;
79-
this.latestVisitedBookmarks$ = this.userDataHistoryStore.getHistory$(
71+
this.latestVisitedResources$ = this.userDataHistoryStore.getHistory$(
8072
this.userId,
8173
1
8274
);
83-
this.latestPinnedBookmarks$ =
84-
this.userDataPinnedStore.getPinnedBookmarks$(this.userId, 1);
75+
this.latestPinnedResources$ =
76+
this.userDataPinnedStore.getPinnedResources$(
77+
this.userId,
78+
1,
79+
this.pinnedQuickAccessLimit
80+
);
8581
});
8682
this.userData$ = this.userDataStore.getUserData$();
8783

@@ -92,15 +88,6 @@ export class AppComponent implements OnInit {
9288
this.showWhatsNewNotification = true;
9389
}
9490
});
95-
96-
this.latestSearches$ = this.userData$.pipe(
97-
map((userData) => {
98-
for (let i = 0; i < 10; i++) {
99-
this.hoveringLastSearches.push(false);
100-
}
101-
return userData.searches.slice(0, 10);
102-
})
103-
);
10491
}
10592
});
10693
this.scrollStrategy = this.scrollStrategyOptions.noop();
@@ -129,7 +116,7 @@ export class AppComponent implements OnInit {
129116
dialogConfig.height = this.getRelativeHeight();
130117
dialogConfig.scrollStrategy = this.scrollStrategy;
131118
dialogConfig.data = {
132-
bookmarks$: this.userDataPinnedStore.getPinnedBookmarks$(this.userId, 1),
119+
bookmarks$: this.userDataPinnedStore.getPinnedResources$(this.userId, 1),
133120
title: '<i class="fas fa-thumbtack"></i> Pinned',
134121
};
135122

@@ -212,16 +199,6 @@ export class AppComponent implements OnInit {
212199
this.userDataStore.updateWelcomeAcknowledge$();
213200
}
214201

215-
resetHoveringLastSearches() {
216-
this.hoveringLastSearches.forEach((item) => (item = false));
217-
}
218-
219-
displaySearchBarSearches() {
220-
this.latestSearchClickNotificationService.sendMessage(
221-
'click on latest searches'
222-
);
223-
}
224-
225202
launchDialogFromQuickAccess(source: string) {
226203
if (source === 'last_visited') {
227204
this.launchHistoryDialog();

0 commit comments

Comments
 (0)