Browse Source

feat: add starred project list in search, refactor variable names

pull/193/head
Trent Larson 2 weeks ago
parent
commit
a51fd90659
  1. 6
      src/db-sql/migration.ts
  2. 9
      src/db/databaseUtil.ts
  3. 15
      src/db/tables/settings.ts
  4. 5
      src/interfaces/records.ts
  5. 10
      src/libs/endorserServer.ts
  6. 7
      src/test/PlatformServiceMixinTest.vue
  7. 14
      src/utils/PlatformServiceMixin.ts
  8. 128
      src/views/DiscoverView.vue
  9. 18
      src/views/HomeView.vue
  10. 26
      src/views/NewActivityView.vue
  11. 45
      src/views/ProjectViewView.vue

6
src/db-sql/migration.ts

@ -125,10 +125,10 @@ const MIGRATIONS = [
`, `,
}, },
{ {
name: "003_add_starredProjectIds_to_settings", name: "005_add_starredPlanHandleIds_to_settings",
sql: ` sql: `
ALTER TABLE settings ADD COLUMN starredProjectIds TEXT; ALTER TABLE settings ADD COLUMN starredPlanHandleIds TEXT DEFAULT '[]'; -- JSON string
ALTER TABLE settings ADD COLUMN lastAckedStarredProjectChangesJwtId TEXT; ALTER TABLE settings ADD COLUMN lastAckedStarredPlanChangesJwtId TEXT;
`, `,
}, },
]; ];

9
src/db/databaseUtil.ts

@ -158,7 +158,10 @@ export async function retrieveSettingsForDefaultAccount(): Promise<Settings> {
result.values, result.values,
)[0] as Settings; )[0] as Settings;
settings.searchBoxes = parseJsonField(settings.searchBoxes, []); settings.searchBoxes = parseJsonField(settings.searchBoxes, []);
settings.starredProjectIds = parseJsonField(settings.starredProjectIds, []); settings.starredPlanHandleIds = parseJsonField(
settings.starredPlanHandleIds,
[],
);
return settings; return settings;
} }
} }
@ -225,8 +228,8 @@ export async function retrieveSettingsForActiveAccount(): Promise<Settings> {
} }
settings.searchBoxes = parseJsonField(settings.searchBoxes, []); settings.searchBoxes = parseJsonField(settings.searchBoxes, []);
settings.starredProjectIds = parseJsonField( settings.starredPlanHandleIds = parseJsonField(
settings.starredProjectIds, settings.starredPlanHandleIds,
[], [],
); );

15
src/db/tables/settings.ts

@ -36,7 +36,7 @@ export type Settings = {
lastAckedOfferToUserJwtId?: string; // the last JWT ID for offer-to-user that they've acknowledged seeing lastAckedOfferToUserJwtId?: string; // the last JWT ID for offer-to-user that they've acknowledged seeing
lastAckedOfferToUserProjectsJwtId?: string; // the last JWT ID for offers-to-user's-projects that they've acknowledged seeing lastAckedOfferToUserProjectsJwtId?: string; // the last JWT ID for offers-to-user's-projects that they've acknowledged seeing
lastAckedStarredProjectChangesJwtId?: string; // the last JWT ID for starred project changes that they've acknowledged seeing lastAckedStarredPlanChangesJwtId?: string; // the last JWT ID for starred plan changes that they've acknowledged seeing
// The claim list has a most recent one used in notifications that's separate from the last viewed // The claim list has a most recent one used in notifications that's separate from the last viewed
lastNotifiedClaimId?: string; lastNotifiedClaimId?: string;
@ -62,19 +62,17 @@ export type Settings = {
showGeneralAdvanced?: boolean; // Show advanced features which don't have their own flag showGeneralAdvanced?: boolean; // Show advanced features which don't have their own flag
showShortcutBvc?: boolean; // Show shortcut for Bountiful Voluntaryist Community actions showShortcutBvc?: boolean; // Show shortcut for Bountiful Voluntaryist Community actions
// List of starred project handleIds starredPlanHandleIds?: string[]; // Array of starred plan handle IDs
starredProjectIds?: Array<string>;
vapid?: string; // VAPID (Voluntary Application Server Identification) field for web push vapid?: string; // VAPID (Voluntary Application Server Identification) field for web push
warnIfProdServer?: boolean; // Warn if using a production server warnIfProdServer?: boolean; // Warn if using a production server
warnIfTestServer?: boolean; // Warn if using a testing server warnIfTestServer?: boolean; // Warn if using a testing server
webPushServer?: string; // Web Push server URL webPushServer?: string; // Web Push server URL
}; };
// type of settings where the searchBoxes are JSON strings instead of objects // type of settings where the values are JSON strings instead of objects
export type SettingsWithJsonStrings = Settings & { export type SettingsWithJsonStrings = Settings & {
searchBoxes: string; searchBoxes: string;
starredProjectIds: string; starredPlanHandleIds: string;
}; };
export function checkIsAnyFeedFilterOn(settings: Settings): boolean { export function checkIsAnyFeedFilterOn(settings: Settings): boolean {
@ -91,6 +89,11 @@ export const SettingsSchema = {
/** /**
* Constants. * Constants.
*/ */
/**
* This is deprecated.
* It only remains for those with a PWA who have not migrated, but we'll soon remove it.
*/
export const MASTER_SETTINGS_KEY = "1"; export const MASTER_SETTINGS_KEY = "1";
export const DEFAULT_PASSKEY_EXPIRATION_MINUTES = 15; export const DEFAULT_PASSKEY_EXPIRATION_MINUTES = 15;

5
src/interfaces/records.ts

@ -93,7 +93,10 @@ export interface PlanData {
name: string; name: string;
/** /**
* The identifier of the project record -- different from jwtId * The identifier of the project record -- different from jwtId
* (Maybe we should use the jwtId to iterate through the records instead.) *
* This has been used to iterate through plan records, because jwtId ordering doesn't match
* chronological create ordering, though it does match most recent edit order (in reverse order).
* (It may be worthwhile to order by jwtId instead. It is an indexed field.)
**/ **/
rowId?: string; rowId?: string;
} }

10
src/libs/endorserServer.ts

@ -793,18 +793,18 @@ export async function getNewOffersToUserProjects(
* @param axios - axios instance * @param axios - axios instance
* @param apiServer - endorser API server URL * @param apiServer - endorser API server URL
* @param activeDid - user's DID for authentication * @param activeDid - user's DID for authentication
* @param starredProjectIds - array of starred project handle IDs * @param starredPlanHandleIds - array of starred project handle IDs
* @param afterId - JWT ID to check for changes after (from lastAckedStarredProjectChangesJwtId) * @param afterId - JWT ID to check for changes after (from lastAckedStarredPlanChangesJwtId)
* @returns { data: Array<PlanSummaryAndPreviousClaim>, hitLimit: boolean } * @returns { data: Array<PlanSummaryAndPreviousClaim>, hitLimit: boolean }
*/ */
export async function getStarredProjectsWithChanges( export async function getStarredProjectsWithChanges(
axios: Axios, axios: Axios,
apiServer: string, apiServer: string,
activeDid: string, activeDid: string,
starredProjectIds: string[], starredPlanHandleIds: string[],
afterId?: string, afterId?: string,
): Promise<{ data: Array<PlanSummaryAndPreviousClaim>; hitLimit: boolean }> { ): Promise<{ data: Array<PlanSummaryAndPreviousClaim>; hitLimit: boolean }> {
if (!starredProjectIds || starredProjectIds.length === 0) { if (!starredPlanHandleIds || starredPlanHandleIds.length === 0) {
return { data: [], hitLimit: false }; return { data: [], hitLimit: false };
} }
@ -817,7 +817,7 @@ export async function getStarredProjectsWithChanges(
const headers = await getHeaders(activeDid); const headers = await getHeaders(activeDid);
const requestBody = { const requestBody = {
planIds: starredProjectIds, planIds: starredPlanHandleIds,
afterId: afterId, afterId: afterId,
}; };

7
src/test/PlatformServiceMixinTest.vue

@ -85,7 +85,6 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
@Component({ @Component({
mixins: [PlatformServiceMixin], mixins: [PlatformServiceMixin],
@ -197,10 +196,10 @@ This tests the helper method only - no database interaction`;
const success = await this.$saveSettings(testSettings); const success = await this.$saveSettings(testSettings);
if (success) { if (success) {
// Now query the raw database to see how it's actually stored // Now query the raw database to see how it's actually stored.
// Note that new users probably have settings with ID of 1 but old migrated users might skip to 2.
const rawResult = await this.$dbQuery( const rawResult = await this.$dbQuery(
"SELECT searchBoxes FROM settings WHERE id = ?", "SELECT searchBoxes FROM settings limit 1",
[MASTER_SETTINGS_KEY],
); );
if (rawResult?.values?.length) { if (rawResult?.values?.length) {

14
src/utils/PlatformServiceMixin.ts

@ -249,13 +249,13 @@ export const PlatformServiceMixin = {
// Keep null values as null // Keep null values as null
} }
// Handle JSON fields like contactMethods // Convert SQLite JSON strings to objects/arrays
if (column === "contactMethods" && typeof value === "string") { if (
try { column === "contactMethods" ||
value = JSON.parse(value); column === "searchBoxes" ||
} catch { column === "starredPlanHandleIds"
value = []; ) {
} value = this._parseJsonField(value, []);
} }
obj[column] = value; obj[column] = value;

128
src/views/DiscoverView.vue

@ -56,16 +56,26 @@
href="#" href="#"
:class="computedStarredTabStyleClassNames()" :class="computedStarredTabStyleClassNames()"
@click=" @click="
projects = [];
userProfiles = [];
isStarredActive = true;
isLocalActive = false; isLocalActive = false;
isMappedActive = false; isMappedActive = false;
isAnywhereActive = false; isAnywhereActive = false;
isStarredActive = true;
isSearchVisible = false; isSearchVisible = false;
tempSearchBox = null; tempSearchBox = null;
loadStarred(); searchStarred();
" "
> >
Starred Starred
<!-- restore when the links don't jump around for different numbers
<span
class="font-semibold text-sm bg-slate-200 px-1.5 py-0.5 rounded-md"
v-if="isLocalActive"
>
{{ localCount > -1 ? localCount : "?" }}
</span>
-->
</a> </a>
</li> </li>
<li> <li>
@ -75,6 +85,7 @@
@click=" @click="
projects = []; projects = [];
userProfiles = []; userProfiles = [];
isStarredActive = false;
isLocalActive = true; isLocalActive = true;
isMappedActive = false; isMappedActive = false;
isAnywhereActive = false; isAnywhereActive = false;
@ -102,6 +113,7 @@
@click=" @click="
projects = []; projects = [];
userProfiles = []; userProfiles = [];
isStarredActive = false;
isLocalActive = false; isLocalActive = false;
isMappedActive = true; isMappedActive = true;
isAnywhereActive = false; isAnywhereActive = false;
@ -122,6 +134,7 @@
@click=" @click="
projects = []; projects = [];
userProfiles = []; userProfiles = [];
isStarredActive = false;
isLocalActive = false; isLocalActive = false;
isMappedActive = false; isMappedActive = false;
isAnywhereActive = true; isAnywhereActive = true;
@ -222,7 +235,9 @@
that search.</span that search.</span
> >
<span v-else-if="isStarredActive"> <span v-else-if="isStarredActive">
<p>You have no starred projects. Star some projects to see them here.</p> <p>
You have no starred projects. Star some projects to see them here.
</p>
<p class="mt-4"> <p class="mt-4">
When you star projects, you will get a notice on the front page when When you star projects, you will get a notice on the front page when
they change. they change.
@ -360,10 +375,8 @@ import {
didInfo, didInfo,
errorStringForLog, errorStringForLog,
getHeaders, getHeaders,
getPlanFromCache,
} from "../libs/endorserServer"; } from "../libs/endorserServer";
import { OnboardPage, retrieveAccountDids } from "../libs/util"; import { OnboardPage, retrieveAccountDids } from "../libs/util";
import { parseJsonField } from "../db/databaseUtil";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { UserProfile } from "@/libs/partnerServer"; import { UserProfile } from "@/libs/partnerServer";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
@ -416,10 +429,12 @@ export default class DiscoverView extends Vue {
allMyDids: Array<string> = []; allMyDids: Array<string> = [];
apiServer = ""; apiServer = "";
isLoading = false; isLoading = false;
isLocalActive = false; isLocalActive = false;
isMappedActive = false; isMappedActive = false;
isAnywhereActive = true; isAnywhereActive = true;
isStarredActive = false; isStarredActive = false;
isProjectsActive = true; isProjectsActive = true;
isPeopleActive = false; isPeopleActive = false;
isSearchVisible = true; isSearchVisible = true;
@ -505,7 +520,7 @@ export default class DiscoverView extends Vue {
}; };
this.requestTiles(mapRef.leafletObject); // not ideal because I found this from experimentation, not documentation this.requestTiles(mapRef.leafletObject); // not ideal because I found this from experimentation, not documentation
} else if (this.isStarredActive) { } else if (this.isStarredActive) {
await this.loadStarred(); await this.searchStarred();
} else { } else {
await this.searchAll(); await this.searchAll();
} }
@ -576,7 +591,7 @@ export default class DiscoverView extends Vue {
} }
} }
public async loadStarred() { public async searchStarred() {
this.resetCounts(); this.resetCounts();
// Clear any previous results // Clear any previous results
@ -588,52 +603,37 @@ export default class DiscoverView extends Vue {
// Get starred project IDs from settings // Get starred project IDs from settings
const settings = await this.$accountSettings(); const settings = await this.$accountSettings();
const starredIds: string[] = parseJsonField(
settings.starredProjectIds,
[],
);
const starredIds = settings.starredPlanHandleIds || [];
if (starredIds.length === 0) { if (starredIds.length === 0) {
// No starred projects // No starred projects
return; return;
} }
// Load each starred project using getPlanFromCache // This could be optimized to only pull those not already in the cache (endorserServer.ts)
const projectPromises = starredIds.map(async (handleId) => {
try {
const project = await getPlanFromCache(
handleId,
this.axios,
this.apiServer,
this.activeDid,
);
if (project) {
// Convert PlanSummaryRecord to PlanData
return {
description: project.description,
handleId: project.handleId,
image: project.image,
issuerDid: project.issuerDid,
name: project.name || UNNAMED_PROJECT,
rowId: project.jwtId,
} as PlanData;
}
return null;
} catch (error) {
logger.warn(`Failed to load starred project ${handleId}:`, error);
return null;
}
});
const projects = await Promise.all(projectPromises);
// Filter out null results and add to projects array const planHandleIdsJson = JSON.stringify(starredIds);
const validProjects = projects.filter( const endpoint =
(project): project is PlanData => this.apiServer +
project !== null && project !== undefined, "/api/v2/report/plans?planHandleIds=" +
); encodeURIComponent(planHandleIdsJson);
const response = await this.axios.get(endpoint, {
headers: await getHeaders(this.activeDid),
});
if (response.status !== 200) {
this.notify.error("Failed to load starred projects", TIMEOUTS.SHORT);
return;
}
const starredPlans: PlanData[] = response.data.data;
if (response.data.hitLimit) {
// someday we'll have to let them incrementally load the rest
this.notify.warning(
"Beware: you have so many starred projects that we cannot load them all.",
TIMEOUTS.SHORT,
);
}
this.projects = validProjects; this.projects = starredPlans;
} catch (error: unknown) { } catch (error: unknown) {
logger.error("Error loading starred projects:", error); logger.error("Error loading starred projects:", error);
this.notify.error( this.notify.error(
@ -738,6 +738,8 @@ export default class DiscoverView extends Vue {
const latestProject = this.projects[this.projects.length - 1]; const latestProject = this.projects[this.projects.length - 1];
if (this.isLocalActive || this.isMappedActive) { if (this.isLocalActive || this.isMappedActive) {
this.searchLocal(latestProject.rowId); this.searchLocal(latestProject.rowId);
} else if (this.isStarredActive) {
this.searchStarred();
} else if (this.isAnywhereActive) { } else if (this.isAnywhereActive) {
this.searchAll(latestProject.rowId); this.searchAll(latestProject.rowId);
} }
@ -881,6 +883,24 @@ export default class DiscoverView extends Vue {
this.$router.push(route); this.$router.push(route);
} }
public computedStarredTabStyleClassNames() {
return {
"inline-block": true,
"py-3": true,
"rounded-t-lg": true,
"border-b-2": true,
active: this.isStarredActive,
"text-black": this.isStarredActive,
"border-black": this.isStarredActive,
"font-semibold": this.isStarredActive,
"text-blue-600": !this.isStarredActive,
"border-transparent": !this.isStarredActive,
"hover:border-slate-400": !this.isStarredActive,
};
}
public computedLocalTabStyleClassNames() { public computedLocalTabStyleClassNames() {
return { return {
"inline-block": true, "inline-block": true,
@ -935,24 +955,6 @@ export default class DiscoverView extends Vue {
}; };
} }
public computedStarredTabStyleClassNames() {
return {
"inline-block": true,
"py-3": true,
"rounded-t-lg": true,
"border-b-2": true,
active: this.isStarredActive,
"text-black": this.isStarredActive,
"border-black": this.isStarredActive,
"font-semibold": this.isStarredActive,
"text-blue-600": !this.isStarredActive,
"border-transparent": !this.isStarredActive,
"hover:border-slate-400": !this.isStarredActive,
};
}
public computedProjectsTabStyleClassNames() { public computedProjectsTabStyleClassNames() {
return { return {
"inline-block": true, "inline-block": true,

18
src/views/HomeView.vue

@ -413,14 +413,14 @@ export default class HomeView extends Vue {
isRegistered = false; isRegistered = false;
lastAckedOfferToUserJwtId?: string; // the last JWT ID for offer-to-user that they've acknowledged seeing lastAckedOfferToUserJwtId?: string; // the last JWT ID for offer-to-user that they've acknowledged seeing
lastAckedOfferToUserProjectsJwtId?: string; // the last JWT ID for offers-to-user's-projects that they've acknowledged seeing lastAckedOfferToUserProjectsJwtId?: string; // the last JWT ID for offers-to-user's-projects that they've acknowledged seeing
lastAckedStarredProjectChangesJwtId?: string; // the last JWT ID for starred project changes that they've acknowledged seeing lastAckedStarredPlanChangesJwtId?: string; // the last JWT ID for starred project changes that they've acknowledged seeing
newOffersToUserHitLimit: boolean = false; newOffersToUserHitLimit: boolean = false;
newOffersToUserProjectsHitLimit: boolean = false; newOffersToUserProjectsHitLimit: boolean = false;
newStarredProjectChangesHitLimit: boolean = false; newStarredProjectChangesHitLimit: boolean = false;
numNewOffersToUser: number = 0; // number of new offers-to-user numNewOffersToUser: number = 0; // number of new offers-to-user
numNewOffersToUserProjects: number = 0; // number of new offers-to-user's-projects numNewOffersToUserProjects: number = 0; // number of new offers-to-user's-projects
numNewStarredProjectChanges: number = 0; // number of new starred project changes numNewStarredProjectChanges: number = 0; // number of new starred project changes
starredProjectIds: Array<string> = []; // list of starred project IDs starredPlanHandleIds: Array<string> = []; // list of starred project IDs
searchBoxes: Array<{ searchBoxes: Array<{
name: string; name: string;
bbox: BoundingBox; bbox: BoundingBox;
@ -565,12 +565,12 @@ export default class HomeView extends Vue {
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId; this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId;
this.lastAckedOfferToUserProjectsJwtId = this.lastAckedOfferToUserProjectsJwtId =
settings.lastAckedOfferToUserProjectsJwtId; settings.lastAckedOfferToUserProjectsJwtId;
this.lastAckedStarredProjectChangesJwtId = this.lastAckedStarredPlanChangesJwtId =
settings.lastAckedStarredProjectChangesJwtId; settings.lastAckedStarredPlanChangesJwtId;
this.searchBoxes = settings.searchBoxes || []; this.searchBoxes = settings.searchBoxes || [];
this.showShortcutBvc = !!settings.showShortcutBvc; this.showShortcutBvc = !!settings.showShortcutBvc;
this.starredProjectIds = databaseUtil.parseJsonField( this.starredPlanHandleIds = databaseUtil.parseJsonField(
settings.starredProjectIds, settings.starredPlanHandleIds,
[], [],
); );
this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings); this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings);
@ -715,14 +715,14 @@ export default class HomeView extends Vue {
* @requires Active DID * @requires Active DID
*/ */
private async loadNewStarredProjectChanges() { private async loadNewStarredProjectChanges() {
if (this.activeDid && this.starredProjectIds.length > 0) { if (this.activeDid && this.starredPlanHandleIds.length > 0) {
try { try {
const starredProjectChanges = await getStarredProjectsWithChanges( const starredProjectChanges = await getStarredProjectsWithChanges(
this.axios, this.axios,
this.apiServer, this.apiServer,
this.activeDid, this.activeDid,
this.starredProjectIds, this.starredPlanHandleIds,
this.lastAckedStarredProjectChangesJwtId, this.lastAckedStarredPlanChangesJwtId,
); );
this.numNewStarredProjectChanges = starredProjectChanges.data.length; this.numNewStarredProjectChanges = starredProjectChanges.data.length;
this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit; this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit;

26
src/views/NewActivityView.vue

@ -338,14 +338,14 @@ export default class NewActivityView extends Vue {
apiServer = ""; apiServer = "";
lastAckedOfferToUserJwtId = ""; lastAckedOfferToUserJwtId = "";
lastAckedOfferToUserProjectsJwtId = ""; lastAckedOfferToUserProjectsJwtId = "";
lastAckedStarredProjectChangesJwtId = ""; lastAckedStarredPlanChangesJwtId = "";
newOffersToUser: Array<OfferSummaryRecord> = []; newOffersToUser: Array<OfferSummaryRecord> = [];
newOffersToUserHitLimit = false; newOffersToUserHitLimit = false;
newOffersToUserProjects: Array<OfferToPlanSummaryRecord> = []; newOffersToUserProjects: Array<OfferToPlanSummaryRecord> = [];
newOffersToUserProjectsHitLimit = false; newOffersToUserProjectsHitLimit = false;
newStarredProjectChanges: Array<PlanSummaryAndPreviousClaim> = []; newStarredProjectChanges: Array<PlanSummaryAndPreviousClaim> = [];
newStarredProjectChangesHitLimit = false; newStarredProjectChangesHitLimit = false;
starredProjectIds: Array<string> = []; starredPlanHandleIds: Array<string> = [];
planDifferences: Record< planDifferences: Record<
string, string,
Record<string, { old: unknown; new: unknown }> Record<string, { old: unknown; new: unknown }>
@ -367,10 +367,10 @@ export default class NewActivityView extends Vue {
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || ""; this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || "";
this.lastAckedOfferToUserProjectsJwtId = this.lastAckedOfferToUserProjectsJwtId =
settings.lastAckedOfferToUserProjectsJwtId || ""; settings.lastAckedOfferToUserProjectsJwtId || "";
this.lastAckedStarredProjectChangesJwtId = this.lastAckedStarredPlanChangesJwtId =
settings.lastAckedStarredProjectChangesJwtId || ""; settings.lastAckedStarredPlanChangesJwtId || "";
this.starredProjectIds = databaseUtil.parseJsonField( this.starredPlanHandleIds = databaseUtil.parseJsonField(
settings.starredProjectIds, settings.starredPlanHandleIds,
[], [],
); );
@ -397,14 +397,14 @@ export default class NewActivityView extends Vue {
this.newOffersToUserProjectsHitLimit = offersToUserProjectsData.hitLimit; this.newOffersToUserProjectsHitLimit = offersToUserProjectsData.hitLimit;
// Load starred project changes if user has starred projects // Load starred project changes if user has starred projects
if (this.starredProjectIds.length > 0) { if (this.starredPlanHandleIds.length > 0) {
try { try {
const starredProjectChangesData = await getStarredProjectsWithChanges( const starredProjectChangesData = await getStarredProjectsWithChanges(
this.axios, this.axios,
this.apiServer, this.apiServer,
this.activeDid, this.activeDid,
this.starredProjectIds, this.starredPlanHandleIds,
this.lastAckedStarredProjectChangesJwtId, this.lastAckedStarredPlanChangesJwtId,
); );
this.newStarredProjectChanges = starredProjectChangesData.data; this.newStarredProjectChanges = starredProjectChangesData.data;
this.newStarredProjectChangesHitLimit = this.newStarredProjectChangesHitLimit =
@ -513,7 +513,7 @@ export default class NewActivityView extends Vue {
this.newStarredProjectChanges.length > 0 this.newStarredProjectChanges.length > 0
) { ) {
await this.$saveUserSettings(this.activeDid, { await this.$saveUserSettings(this.activeDid, {
lastAckedStarredProjectChangesJwtId: lastAckedStarredPlanChangesJwtId:
this.newStarredProjectChanges[0].plan.jwtId, this.newStarredProjectChanges[0].plan.jwtId,
}); });
this.notify.info( this.notify.info(
@ -530,14 +530,14 @@ export default class NewActivityView extends Vue {
if (index !== -1 && index < this.newStarredProjectChanges.length - 1) { if (index !== -1 && index < this.newStarredProjectChanges.length - 1) {
// Set to the next change's jwtId // Set to the next change's jwtId
await this.$saveUserSettings(this.activeDid, { await this.$saveUserSettings(this.activeDid, {
lastAckedStarredProjectChangesJwtId: lastAckedStarredPlanChangesJwtId:
this.newStarredProjectChanges[index + 1].plan.jwtId, this.newStarredProjectChanges[index + 1].plan.jwtId,
}); });
} else { } else {
// it's the last entry (or not found), so just keep it the same // it's the last entry (or not found), so just keep it the same
await this.$saveUserSettings(this.activeDid, { await this.$saveUserSettings(this.activeDid, {
lastAckedStarredProjectChangesJwtId: lastAckedStarredPlanChangesJwtId:
this.lastAckedStarredProjectChangesJwtId, this.lastAckedStarredPlanChangesJwtId,
}); });
} }
this.notify.info( this.notify.info(

45
src/views/ProjectViewView.vue

@ -27,12 +27,6 @@
> >
<font-awesome icon="pen" class="text-sm text-blue-500 ml-2 mb-1" /> <font-awesome icon="pen" class="text-sm text-blue-500 ml-2 mb-1" />
</button> </button>
<button title="Copy Link to Project" @click="onCopyLinkClick()">
<font-awesome
icon="link"
class="text-sm text-slate-500 ml-2 mb-1"
/>
</button>
<button <button
:title=" :title="
isStarred isStarred
@ -830,11 +824,8 @@ export default class ProjectViewView extends Vue {
this.loadTotals(); this.loadTotals();
// Check if this project is starred when settings are loaded // Check if this project is starred when settings are loaded
if (this.projectId && settings.starredProjectIds) { if (this.projectId && settings.starredPlanHandleIds) {
const starredIds: string[] = databaseUtil.parseJsonField( const starredIds = settings.starredPlanHandleIds || [];
settings.starredProjectIds,
[],
);
this.isStarred = starredIds.includes(this.projectId); this.isStarred = starredIds.includes(this.projectId);
} }
} }
@ -1513,10 +1504,7 @@ export default class ProjectViewView extends Vue {
if (!this.isStarred) { if (!this.isStarred) {
// Add to starred projects // Add to starred projects
const settings = await databaseUtil.retrieveSettingsForActiveAccount(); const settings = await databaseUtil.retrieveSettingsForActiveAccount();
const starredIds: string[] = databaseUtil.parseJsonField( const starredIds = settings.starredPlanHandleIds || [];
settings.starredProjectIds,
[],
);
if (!starredIds.includes(this.projectId)) { if (!starredIds.includes(this.projectId)) {
const newStarredIds = [...starredIds, this.projectId]; const newStarredIds = [...starredIds, this.projectId];
@ -1524,43 +1512,42 @@ export default class ProjectViewView extends Vue {
const result = await databaseUtil.updateDidSpecificSettings( const result = await databaseUtil.updateDidSpecificSettings(
this.activeDid, this.activeDid,
// @ts-expect-error until we use SettingsWithJsonString properly // @ts-expect-error until we use SettingsWithJsonString properly
{ starredProjectIds: newIdsParam }, { starredPlanHandleIds: newIdsParam },
); );
if (!result) { if (result) {
this.isStarred = true;
} else {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log( console.log(
"Still getting a bad result from SQL update to star a project.", "Still getting a bad result from SQL update to star a project.",
); );
} }
} }
if (!settings.lastAckedStarredProjectChangesJwtId) { if (!settings.lastAckedStarredPlanChangesJwtId) {
await databaseUtil.updateDidSpecificSettings(this.activeDid, { await databaseUtil.updateDidSpecificSettings(this.activeDid, {
lastAckedStarredProjectChangesJwtId: settings.lastViewedClaimId, lastAckedStarredPlanChangesJwtId: settings.lastViewedClaimId,
}); });
} }
this.isStarred = true;
} else { } else {
// Remove from starred projects // Remove from starred projects
const settings = await databaseUtil.retrieveSettingsForActiveAccount(); const settings = await databaseUtil.retrieveSettingsForActiveAccount();
const starredIds: string[] = databaseUtil.parseJsonField( const starredIds = settings.starredPlanHandleIds || [];
settings.starredProjectIds,
[],
);
const updatedIds = starredIds.filter((id) => id !== this.projectId); const updatedIds = starredIds.filter((id) => id !== this.projectId);
const newIdsParam = JSON.stringify(updatedIds); const newIdsParam = JSON.stringify(updatedIds);
const result = await databaseUtil.updateDidSpecificSettings( const result = await databaseUtil.updateDidSpecificSettings(
this.activeDid, this.activeDid,
// @ts-expect-error until we use SettingsWithJsonString properly // @ts-expect-error until we use SettingsWithJsonString properly
{ starredProjectIds: newIdsParam }, { starredPlanHandleIds: newIdsParam },
); );
if (!result) { if (result) {
this.isStarred = false;
} else {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log( logger.error(
"Still getting a bad result from SQL update to unstar a project.", "Got a bad result from SQL update to unstar a project.",
); );
} }
this.isStarred = false;
} }
} catch (error) { } catch (error) {
logger.error("Error toggling star status:", error); logger.error("Error toggling star status:", error);

Loading…
Cancel
Save