feat: add starred projects at the top of the list to choose
This commit is contained in:
18
package-lock.json
generated
18
package-lock.json
generated
@@ -27,7 +27,7 @@
|
||||
"@ethersproject/hdnode": "^5.7.0",
|
||||
"@ethersproject/wallet": "^5.8.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.1",
|
||||
"@fortawesome/free-brands-svg-icons": "^7.1.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.5.1",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.6",
|
||||
@@ -6791,24 +6791,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/free-brands-svg-icons": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-7.1.0.tgz",
|
||||
"integrity": "sha512-9byUd9bgNfthsZAjBl6GxOu1VPHgBuRUP9juI7ZoM98h8xNPTCTagfwUFyYscdZq4Hr7gD1azMfM9s5tIWKZZA==",
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.7.2.tgz",
|
||||
"integrity": "sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q==",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "7.1.0"
|
||||
"@fortawesome/fontawesome-common-types": "6.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/free-brands-svg-icons/node_modules/@fortawesome/fontawesome-common-types": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.1.0.tgz",
|
||||
"integrity": "sha512-l/BQM7fYntsCI//du+6sEnHOP6a74UixFyOYUyz2DLMXKx+6DEhfR3F2NYGE45XH1JJuIamacb4IZs9S0ZOWLA==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/free-regular-svg-icons": {
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.2.tgz",
|
||||
|
||||
@@ -139,17 +139,62 @@ projects, and special entities with selection. * * @author Matthew Raymer */
|
||||
</template>
|
||||
|
||||
<template v-else-if="entityType === 'projects'">
|
||||
<ProjectCard
|
||||
v-for="project in displayedEntities as PlanData[]"
|
||||
:key="project.handleId"
|
||||
:project="project"
|
||||
:active-did="activeDid"
|
||||
:all-my-dids="allMyDids"
|
||||
:all-contacts="allContacts"
|
||||
:notify="notify"
|
||||
:conflict-context="conflictContext"
|
||||
@project-selected="handleProjectSelected"
|
||||
/>
|
||||
<!-- When showing projects without search: split into recently bookmarked and rest -->
|
||||
<template v-if="!searchTerm.trim()">
|
||||
<!-- Recently Bookmarked Section -->
|
||||
<template v-if="recentBookmarkedProjects.length > 0">
|
||||
<li
|
||||
class="text-xs font-semibold text-slate-500 uppercase pt-5 pb-1.5 px-2 border-b border-slate-300"
|
||||
>
|
||||
Recently Bookmarked
|
||||
</li>
|
||||
<ProjectCard
|
||||
v-for="project in recentBookmarkedProjects"
|
||||
:key="project.handleId"
|
||||
:project="project"
|
||||
:active-did="activeDid"
|
||||
:all-my-dids="allMyDids"
|
||||
:all-contacts="allContacts"
|
||||
:notify="notify"
|
||||
:conflict-context="conflictContext"
|
||||
@project-selected="handleProjectSelected"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Rest of Projects Section -->
|
||||
<li
|
||||
v-if="recentBookmarkedProjects.length > 0"
|
||||
class="text-xs font-semibold text-slate-500 uppercase pt-5 pb-1.5 px-2 border-b border-slate-300"
|
||||
>
|
||||
All Projects
|
||||
</li>
|
||||
<ProjectCard
|
||||
v-for="project in remainingProjects"
|
||||
:key="project.handleId"
|
||||
:project="project"
|
||||
:active-did="activeDid"
|
||||
:all-my-dids="allMyDids"
|
||||
:all-contacts="allContacts"
|
||||
:notify="notify"
|
||||
:conflict-context="conflictContext"
|
||||
@project-selected="handleProjectSelected"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- When searching: show filtered results normally -->
|
||||
<template v-else>
|
||||
<ProjectCard
|
||||
v-for="project in displayedEntities as PlanData[]"
|
||||
:key="project.handleId"
|
||||
:project="project"
|
||||
:active-did="activeDid"
|
||||
:all-my-dids="allMyDids"
|
||||
:all-contacts="allContacts"
|
||||
:notify="notify"
|
||||
:conflict-context="conflictContext"
|
||||
@project-selected="handleProjectSelected"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</ul>
|
||||
</template>
|
||||
@@ -175,6 +220,7 @@ import { TIMEOUTS } from "@/utils/notify";
|
||||
const INITIAL_BATCH_SIZE = 20;
|
||||
const INCREMENT_SIZE = 20;
|
||||
const RECENT_CONTACTS_COUNT = 3;
|
||||
const RECENT_BOOKMARKED_PROJECTS_COUNT = 3;
|
||||
|
||||
/**
|
||||
* EntityGrid - Unified grid layout for displaying people or projects
|
||||
@@ -223,6 +269,9 @@ export default class EntityGrid extends Vue {
|
||||
infiniteScrollReset?: () => void;
|
||||
scrollContainer?: HTMLElement;
|
||||
|
||||
// Starred projects state (for showing recently bookmarked projects)
|
||||
starredPlanHandleIds: string[] = [];
|
||||
|
||||
/**
|
||||
* Array of entities to display
|
||||
*
|
||||
@@ -378,7 +427,8 @@ export default class EntityGrid extends Vue {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remaining contacts sorted alphabetically (when showing contacts and not searching)
|
||||
* Get all contacts sorted alphabetically (when showing contacts and not searching)
|
||||
* Includes contacts shown in "Recently Added" section as well
|
||||
* Uses infinite scroll to control how many are displayed
|
||||
*/
|
||||
get alphabeticalContacts(): Contact[] {
|
||||
@@ -389,18 +439,68 @@ export default class EntityGrid extends Vue {
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
// Skip the first few (recent contacts) and sort the rest alphabetically
|
||||
// Sort all contacts alphabetically (including recent ones)
|
||||
// Create a copy to avoid mutating the original array
|
||||
const remaining = this.entities as Contact[];
|
||||
const sorted = [...remaining].sort((a: Contact, b: Contact) => {
|
||||
// Sort alphabetically by name, falling back to DID if name is missing
|
||||
const nameA = (a.name || a.did).toLowerCase();
|
||||
const nameB = (b.name || b.did).toLowerCase();
|
||||
return nameA.localeCompare(nameB);
|
||||
});
|
||||
// Apply infinite scroll: show based on displayedCount (minus the recent contacts)
|
||||
const toShow = Math.max(0, this.displayedCount - RECENT_CONTACTS_COUNT);
|
||||
return sorted.slice(0, toShow);
|
||||
const sorted = [...(this.entities as Contact[])].sort(
|
||||
(a: Contact, b: Contact) => {
|
||||
// Sort alphabetically by name, falling back to DID if name is missing
|
||||
const nameA = (a.name || a.did).toLowerCase();
|
||||
const nameB = (b.name || b.did).toLowerCase();
|
||||
return nameA.localeCompare(nameB);
|
||||
},
|
||||
);
|
||||
// Apply infinite scroll: show based on displayedCount
|
||||
return sorted.slice(0, this.displayedCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 3 most recently bookmarked projects (when showing projects and not searching)
|
||||
* The starredPlanHandleIds array order represents bookmark order (newest at the end)
|
||||
*/
|
||||
get recentBookmarkedProjects(): PlanData[] {
|
||||
if (
|
||||
this.entityType !== "projects" ||
|
||||
this.searchTerm.trim() ||
|
||||
this.starredPlanHandleIds.length === 0
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const projects = this.entitiesToUse as PlanData[];
|
||||
if (projects.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get the last 3 starred IDs (most recently bookmarked)
|
||||
const recentStarredIds = this.starredPlanHandleIds.slice(
|
||||
-RECENT_BOOKMARKED_PROJECTS_COUNT,
|
||||
);
|
||||
|
||||
// Find projects matching those IDs, preserving the order
|
||||
const recentProjects = recentStarredIds
|
||||
.map((id) => projects.find((p) => p.handleId === id))
|
||||
.filter((p): p is PlanData => p !== undefined);
|
||||
|
||||
return recentProjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all projects (when showing projects and not searching)
|
||||
* Includes projects shown in "Recently Bookmarked" section as well
|
||||
* Uses infinite scroll to control how many are displayed
|
||||
*/
|
||||
get remainingProjects(): PlanData[] {
|
||||
if (this.entityType !== "projects" || this.searchTerm.trim()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const projects = this.entitiesToUse as PlanData[];
|
||||
if (projects.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Apply infinite scroll: show based on displayedCount
|
||||
return projects.slice(0, this.displayedCount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -793,12 +893,11 @@ export default class EntityGrid extends Vue {
|
||||
}
|
||||
|
||||
// People: check if more alphabetical contacts available
|
||||
// Total available = recent + all alphabetical
|
||||
// All contacts are shown alphabetically (recent ones appear in both sections)
|
||||
if (!this.entities) {
|
||||
return false;
|
||||
}
|
||||
const totalAvailable = RECENT_CONTACTS_COUNT + this.entities.length;
|
||||
return this.displayedCount < totalAvailable;
|
||||
return this.displayedCount < this.entities.length;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -810,6 +909,9 @@ export default class EntityGrid extends Vue {
|
||||
const settings = await this.$accountSettings();
|
||||
this.apiServer = settings.apiServer || "";
|
||||
|
||||
// Load starred project IDs for showing recently bookmarked projects
|
||||
this.starredPlanHandleIds = settings.starredPlanHandleIds || [];
|
||||
|
||||
// Load projects on mount if entities prop not provided
|
||||
if (!this.entities && this.apiServer) {
|
||||
this.isLoadingProjects = true;
|
||||
@@ -959,7 +1061,7 @@ export default class EntityGrid extends Vue {
|
||||
this.displayedCount = INITIAL_BATCH_SIZE;
|
||||
this.infiniteScrollReset?.();
|
||||
|
||||
// For projects: if entities prop is provided, clear internal state
|
||||
// For projects: clear internal state if entities prop is provided
|
||||
if (this.entityType === "projects" && this.entities) {
|
||||
this.allProjects = [];
|
||||
this.loadBeforeId = undefined;
|
||||
|
||||
Reference in New Issue
Block a user