forked from jsnbuchanan/crowd-funder-for-time-pwa
feat: add a notification for changes to starred projects
This commit is contained in:
@@ -36,6 +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
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -61,7 +62,7 @@ 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 IDs, which are recommended to be handleIds
|
// List of starred project handleIds
|
||||||
starredProjectIds?: Array<string>;
|
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
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { GiveActionClaim, OfferClaim } from "./claims";
|
import { GiveActionClaim, OfferClaim } from "./claims";
|
||||||
|
import { ClaimObject } from "./common";
|
||||||
|
|
||||||
// a summary record; the VC is found the fullClaim field
|
// a summary record; the VC is found the fullClaim field
|
||||||
export interface GiveSummaryRecord {
|
export interface GiveSummaryRecord {
|
||||||
@@ -61,6 +62,11 @@ export interface PlanSummaryRecord {
|
|||||||
jwtId?: string;
|
jwtId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PlanSummaryAndPreviousClaim {
|
||||||
|
plan: PlanSummaryRecord;
|
||||||
|
wrappedClaimBefore: ClaimObject;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents data about a project
|
* Represents data about a project
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -56,7 +56,12 @@ import {
|
|||||||
KeyMetaWithPrivate,
|
KeyMetaWithPrivate,
|
||||||
KeyMetaMaybeWithPrivate,
|
KeyMetaMaybeWithPrivate,
|
||||||
} from "../interfaces/common";
|
} from "../interfaces/common";
|
||||||
import { PlanSummaryRecord } from "../interfaces/records";
|
import {
|
||||||
|
OfferSummaryRecord,
|
||||||
|
OfferToPlanSummaryRecord,
|
||||||
|
PlanSummaryAndPreviousClaim,
|
||||||
|
PlanSummaryRecord,
|
||||||
|
} from "../interfaces/records";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||||
import { APP_SERVER } from "@/constants/app";
|
import { APP_SERVER } from "@/constants/app";
|
||||||
@@ -730,7 +735,7 @@ export async function getNewOffersToUser(
|
|||||||
activeDid: string,
|
activeDid: string,
|
||||||
afterOfferJwtId?: string,
|
afterOfferJwtId?: string,
|
||||||
beforeOfferJwtId?: string,
|
beforeOfferJwtId?: string,
|
||||||
) {
|
): Promise<{ data: Array<OfferSummaryRecord>; hitLimit: boolean }> {
|
||||||
let url = `${apiServer}/api/v2/report/offers?recipientDid=${activeDid}`;
|
let url = `${apiServer}/api/v2/report/offers?recipientDid=${activeDid}`;
|
||||||
if (afterOfferJwtId) {
|
if (afterOfferJwtId) {
|
||||||
url += "&afterId=" + afterOfferJwtId;
|
url += "&afterId=" + afterOfferJwtId;
|
||||||
@@ -752,7 +757,7 @@ export async function getNewOffersToUserProjects(
|
|||||||
activeDid: string,
|
activeDid: string,
|
||||||
afterOfferJwtId?: string,
|
afterOfferJwtId?: string,
|
||||||
beforeOfferJwtId?: string,
|
beforeOfferJwtId?: string,
|
||||||
) {
|
): Promise<{ data: Array<OfferToPlanSummaryRecord>; hitLimit: boolean }> {
|
||||||
let url = `${apiServer}/api/v2/report/offersToPlansOwnedByMe`;
|
let url = `${apiServer}/api/v2/report/offersToPlansOwnedByMe`;
|
||||||
if (afterOfferJwtId) {
|
if (afterOfferJwtId) {
|
||||||
url += "?afterId=" + afterOfferJwtId;
|
url += "?afterId=" + afterOfferJwtId;
|
||||||
@@ -766,6 +771,44 @@ export async function getNewOffersToUserProjects(
|
|||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get starred projects that have been updated since the last check
|
||||||
|
*
|
||||||
|
* @param axios - axios instance
|
||||||
|
* @param apiServer - endorser API server URL
|
||||||
|
* @param activeDid - user's DID for authentication
|
||||||
|
* @param starredProjectIds - array of starred project handle IDs
|
||||||
|
* @param afterId - JWT ID to check for changes after (from lastAckedStarredProjectChangesJwtId)
|
||||||
|
* @returns { data: Array<PlanSummaryAndPreviousClaim>, hitLimit: boolean }
|
||||||
|
*/
|
||||||
|
export async function getStarredProjectsWithChanges(
|
||||||
|
axios: Axios,
|
||||||
|
apiServer: string,
|
||||||
|
activeDid: string,
|
||||||
|
starredProjectIds: string[],
|
||||||
|
afterId?: string,
|
||||||
|
): Promise<{ data: Array<PlanSummaryAndPreviousClaim>; hitLimit: boolean }> {
|
||||||
|
if (!starredProjectIds || starredProjectIds.length === 0) {
|
||||||
|
return { data: [], hitLimit: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!afterId) {
|
||||||
|
return { data: [], hitLimit: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use POST method for larger lists of project IDs
|
||||||
|
const url = `${apiServer}/api/v2/report/plansLastUpdatedBetween`;
|
||||||
|
const headers = await getHeaders(activeDid);
|
||||||
|
|
||||||
|
const requestBody = {
|
||||||
|
planIds: starredProjectIds,
|
||||||
|
afterId: afterId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await axios.post(url, requestBody, { headers });
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct GiveAction VC for submission to server
|
* Construct GiveAction VC for submission to server
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -201,6 +201,22 @@ Raymer * @version 1.0.0 */
|
|||||||
projects
|
projects
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="numNewStarredProjectChanges"
|
||||||
|
class="bg-gradient-to-b from-yellow-400 to-yellow-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] m-1 px-4 py-4 rounded-md text-white"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="block text-center text-6xl"
|
||||||
|
data-testId="newStarredProjectChangesActivityNumber"
|
||||||
|
>
|
||||||
|
{{ numNewStarredProjectChanges
|
||||||
|
}}{{ newStarredProjectChangesHitLimit ? "+" : "" }}
|
||||||
|
</span>
|
||||||
|
<p class="text-center">
|
||||||
|
starred project{{ numNewStarredProjectChanges === 1 ? "" : "s" }}
|
||||||
|
with changes
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end mt-2">
|
<div class="flex justify-end mt-2">
|
||||||
<button class="text-blue-500">View All New Activity For You</button>
|
<button class="text-blue-500">View All New Activity For You</button>
|
||||||
@@ -268,6 +284,7 @@ import {
|
|||||||
getHeaders,
|
getHeaders,
|
||||||
getNewOffersToUser,
|
getNewOffersToUser,
|
||||||
getNewOffersToUserProjects,
|
getNewOffersToUserProjects,
|
||||||
|
getStarredProjectsWithChanges,
|
||||||
getPlanFromCache,
|
getPlanFromCache,
|
||||||
} from "../libs/endorserServer";
|
} from "../libs/endorserServer";
|
||||||
import {
|
import {
|
||||||
@@ -283,6 +300,7 @@ import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
|||||||
import { NOTIFY_CONTACT_LOADING_ISSUE } from "@/constants/notifications";
|
import { NOTIFY_CONTACT_LOADING_ISSUE } from "@/constants/notifications";
|
||||||
import * as Package from "../../package.json";
|
import * as Package from "../../package.json";
|
||||||
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
import * as databaseUtil from "../db/databaseUtil";
|
||||||
|
|
||||||
// consolidate this with GiveActionClaim in src/interfaces/claims.ts
|
// consolidate this with GiveActionClaim in src/interfaces/claims.ts
|
||||||
interface Claim {
|
interface Claim {
|
||||||
@@ -395,10 +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
|
||||||
newOffersToUserHitLimit: boolean = false;
|
newOffersToUserHitLimit: boolean = false;
|
||||||
newOffersToUserProjectsHitLimit: boolean = false;
|
newOffersToUserProjectsHitLimit: 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
|
||||||
|
starredProjectIds: Array<string> = []; // list of starred project IDs
|
||||||
searchBoxes: Array<{
|
searchBoxes: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
bbox: BoundingBox;
|
bbox: BoundingBox;
|
||||||
@@ -438,6 +460,7 @@ export default class HomeView extends Vue {
|
|||||||
// Registration check already handled in initializeIdentity()
|
// Registration check already handled in initializeIdentity()
|
||||||
await this.loadFeedData();
|
await this.loadFeedData();
|
||||||
await this.loadNewOffers();
|
await this.loadNewOffers();
|
||||||
|
await this.loadNewStarredProjectChanges();
|
||||||
await this.checkOnboarding();
|
await this.checkOnboarding();
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
this.handleError(err);
|
this.handleError(err);
|
||||||
@@ -542,8 +565,14 @@ export default class HomeView extends Vue {
|
|||||||
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId;
|
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId;
|
||||||
this.lastAckedOfferToUserProjectsJwtId =
|
this.lastAckedOfferToUserProjectsJwtId =
|
||||||
settings.lastAckedOfferToUserProjectsJwtId;
|
settings.lastAckedOfferToUserProjectsJwtId;
|
||||||
|
this.lastAckedStarredProjectChangesJwtId =
|
||||||
|
settings.lastAckedStarredProjectChangesJwtId;
|
||||||
this.searchBoxes = settings.searchBoxes || [];
|
this.searchBoxes = settings.searchBoxes || [];
|
||||||
this.showShortcutBvc = !!settings.showShortcutBvc;
|
this.showShortcutBvc = !!settings.showShortcutBvc;
|
||||||
|
this.starredProjectIds = databaseUtil.parseJsonField(
|
||||||
|
settings.starredProjectIds,
|
||||||
|
[],
|
||||||
|
);
|
||||||
this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings);
|
this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings);
|
||||||
|
|
||||||
// Check onboarding status
|
// Check onboarding status
|
||||||
@@ -675,6 +704,43 @@ export default class HomeView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads new changes for starred projects
|
||||||
|
* Updates:
|
||||||
|
* - Number of new starred project changes
|
||||||
|
* - Rate limit status for starred project changes
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* Called by mounted() and initializeIdentity()
|
||||||
|
* @requires Active DID
|
||||||
|
*/
|
||||||
|
private async loadNewStarredProjectChanges() {
|
||||||
|
if (this.activeDid && this.starredProjectIds.length > 0) {
|
||||||
|
try {
|
||||||
|
const starredProjectChanges = await getStarredProjectsWithChanges(
|
||||||
|
this.axios,
|
||||||
|
this.apiServer,
|
||||||
|
this.activeDid,
|
||||||
|
this.starredProjectIds,
|
||||||
|
this.lastAckedStarredProjectChangesJwtId,
|
||||||
|
);
|
||||||
|
this.numNewStarredProjectChanges = starredProjectChanges.data.length;
|
||||||
|
this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit;
|
||||||
|
} catch (error) {
|
||||||
|
// Don't show errors for starred project changes as it's a secondary feature
|
||||||
|
logger.warn(
|
||||||
|
"[HomeView] Failed to load starred project changes:",
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
this.numNewStarredProjectChanges = 0;
|
||||||
|
this.newStarredProjectChangesHitLimit = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.numNewStarredProjectChanges = 0;
|
||||||
|
this.newStarredProjectChangesHitLimit = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if user needs onboarding using ultra-concise mixin utilities
|
* Checks if user needs onboarding using ultra-concise mixin utilities
|
||||||
* Opens onboarding dialog if not completed
|
* Opens onboarding dialog if not completed
|
||||||
|
|||||||
@@ -144,6 +144,73 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Starred Projects with Changes Section -->
|
||||||
|
<div
|
||||||
|
class="flex justify-between mt-6"
|
||||||
|
data-testId="showStarredProjectChanges"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<span class="text-lg font-medium"
|
||||||
|
>{{ newStarredProjectChanges.length
|
||||||
|
}}{{ newStarredProjectChangesHitLimit ? "+" : "" }}</span
|
||||||
|
>
|
||||||
|
<span class="text-lg font-medium ml-4"
|
||||||
|
>Starred Project{{
|
||||||
|
newStarredProjectChanges.length === 1 ? "" : "s"
|
||||||
|
}}
|
||||||
|
With Changes</span
|
||||||
|
>
|
||||||
|
<font-awesome
|
||||||
|
v-if="newStarredProjectChanges.length > 0"
|
||||||
|
:icon="
|
||||||
|
showStarredProjectChangesDetails ? 'chevron-down' : 'chevron-right'
|
||||||
|
"
|
||||||
|
class="cursor-pointer ml-4 mr-4 text-lg"
|
||||||
|
@click="expandStarredProjectChangesAndMarkRead()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showStarredProjectChangesDetails" class="ml-4 mt-4">
|
||||||
|
<ul class="list-disc ml-4">
|
||||||
|
<li
|
||||||
|
v-for="projectChange in newStarredProjectChanges"
|
||||||
|
:key="projectChange.plan.handleId"
|
||||||
|
class="mt-4 relative group"
|
||||||
|
>
|
||||||
|
<span class="font-medium">{{
|
||||||
|
projectChange.plan.name || "Unnamed Project"
|
||||||
|
}}</span>
|
||||||
|
<span v-if="projectChange.plan.description" class="text-gray-600">
|
||||||
|
- {{ projectChange.plan.description }}
|
||||||
|
</span>
|
||||||
|
<router-link
|
||||||
|
:to="{
|
||||||
|
path: '/plan/' + encodeURIComponent(projectChange.plan.handleId),
|
||||||
|
}"
|
||||||
|
class="text-blue-500"
|
||||||
|
>
|
||||||
|
<font-awesome
|
||||||
|
icon="external-link-alt"
|
||||||
|
class="pl-2 text-blue-500 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</router-link>
|
||||||
|
<!-- New line that appears on hover -->
|
||||||
|
<div
|
||||||
|
class="absolute left-0 w-full text-left text-gray-500 text-sm hidden group-hover:flex cursor-pointer items-center"
|
||||||
|
@click="
|
||||||
|
markStarredProjectChangesAsReadStartingWith(
|
||||||
|
projectChange.plan.handleId,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span class="inline-block w-8 h-px bg-gray-500 mr-2" />
|
||||||
|
Click to keep all above as new changes
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -159,17 +226,20 @@ import { Router } from "vue-router";
|
|||||||
import {
|
import {
|
||||||
OfferSummaryRecord,
|
OfferSummaryRecord,
|
||||||
OfferToPlanSummaryRecord,
|
OfferToPlanSummaryRecord,
|
||||||
|
PlanSummaryAndPreviousClaim,
|
||||||
} from "../interfaces/records";
|
} from "../interfaces/records";
|
||||||
import {
|
import {
|
||||||
didInfo,
|
didInfo,
|
||||||
displayAmount,
|
displayAmount,
|
||||||
getNewOffersToUser,
|
getNewOffersToUser,
|
||||||
getNewOffersToUserProjects,
|
getNewOffersToUserProjects,
|
||||||
|
getStarredProjectsWithChanges,
|
||||||
} from "../libs/endorserServer";
|
} from "../libs/endorserServer";
|
||||||
import { retrieveAccountDids } from "../libs/util";
|
import { retrieveAccountDids } from "../libs/util";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
|
import * as databaseUtil from "../db/databaseUtil";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: { GiftedDialog, QuickNav, EntityIcon },
|
components: { GiftedDialog, QuickNav, EntityIcon },
|
||||||
@@ -186,13 +256,18 @@ export default class NewActivityView extends Vue {
|
|||||||
apiServer = "";
|
apiServer = "";
|
||||||
lastAckedOfferToUserJwtId = "";
|
lastAckedOfferToUserJwtId = "";
|
||||||
lastAckedOfferToUserProjectsJwtId = "";
|
lastAckedOfferToUserProjectsJwtId = "";
|
||||||
|
lastAckedStarredProjectChangesJwtId = "";
|
||||||
newOffersToUser: Array<OfferSummaryRecord> = [];
|
newOffersToUser: Array<OfferSummaryRecord> = [];
|
||||||
newOffersToUserHitLimit = false;
|
newOffersToUserHitLimit = false;
|
||||||
newOffersToUserProjects: Array<OfferToPlanSummaryRecord> = [];
|
newOffersToUserProjects: Array<OfferToPlanSummaryRecord> = [];
|
||||||
newOffersToUserProjectsHitLimit = false;
|
newOffersToUserProjectsHitLimit = false;
|
||||||
|
newStarredProjectChanges: Array<PlanSummaryAndPreviousClaim> = [];
|
||||||
|
newStarredProjectChangesHitLimit = false;
|
||||||
|
starredProjectIds: Array<string> = [];
|
||||||
|
|
||||||
showOffersDetails = false;
|
showOffersDetails = false;
|
||||||
showOffersToUserProjectsDetails = false;
|
showOffersToUserProjectsDetails = false;
|
||||||
|
showStarredProjectChangesDetails = false;
|
||||||
didInfo = didInfo;
|
didInfo = didInfo;
|
||||||
displayAmount = displayAmount;
|
displayAmount = displayAmount;
|
||||||
|
|
||||||
@@ -206,6 +281,12 @@ export default class NewActivityView extends Vue {
|
|||||||
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || "";
|
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || "";
|
||||||
this.lastAckedOfferToUserProjectsJwtId =
|
this.lastAckedOfferToUserProjectsJwtId =
|
||||||
settings.lastAckedOfferToUserProjectsJwtId || "";
|
settings.lastAckedOfferToUserProjectsJwtId || "";
|
||||||
|
this.lastAckedStarredProjectChangesJwtId =
|
||||||
|
settings.lastAckedStarredProjectChangesJwtId || "";
|
||||||
|
this.starredProjectIds = databaseUtil.parseJsonField(
|
||||||
|
settings.starredProjectIds,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
this.allContacts = await this.$getAllContacts();
|
this.allContacts = await this.$getAllContacts();
|
||||||
|
|
||||||
@@ -229,6 +310,26 @@ export default class NewActivityView extends Vue {
|
|||||||
this.newOffersToUserProjects = offersToUserProjectsData.data;
|
this.newOffersToUserProjects = offersToUserProjectsData.data;
|
||||||
this.newOffersToUserProjectsHitLimit = offersToUserProjectsData.hitLimit;
|
this.newOffersToUserProjectsHitLimit = offersToUserProjectsData.hitLimit;
|
||||||
|
|
||||||
|
// Load starred project changes if user has starred projects
|
||||||
|
if (this.starredProjectIds.length > 0) {
|
||||||
|
try {
|
||||||
|
const starredProjectChangesData = await getStarredProjectsWithChanges(
|
||||||
|
this.axios,
|
||||||
|
this.apiServer,
|
||||||
|
this.activeDid,
|
||||||
|
this.starredProjectIds,
|
||||||
|
this.lastAckedStarredProjectChangesJwtId,
|
||||||
|
);
|
||||||
|
this.newStarredProjectChanges = starredProjectChangesData.data;
|
||||||
|
this.newStarredProjectChangesHitLimit =
|
||||||
|
starredProjectChangesData.hitLimit;
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn("Failed to load starred project changes:", error);
|
||||||
|
this.newStarredProjectChanges = [];
|
||||||
|
this.newStarredProjectChangesHitLimit = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
logger.error("Error retrieving settings & contacts:", err);
|
logger.error("Error retrieving settings & contacts:", err);
|
||||||
@@ -314,5 +415,46 @@ export default class NewActivityView extends Vue {
|
|||||||
TIMEOUTS.STANDARD,
|
TIMEOUTS.STANDARD,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async expandStarredProjectChangesAndMarkRead() {
|
||||||
|
this.showStarredProjectChangesDetails =
|
||||||
|
!this.showStarredProjectChangesDetails;
|
||||||
|
if (
|
||||||
|
this.showStarredProjectChangesDetails &&
|
||||||
|
this.newStarredProjectChanges.length > 0
|
||||||
|
) {
|
||||||
|
await this.$updateSettings({
|
||||||
|
lastAckedStarredProjectChangesJwtId:
|
||||||
|
this.newStarredProjectChanges[0].plan.jwtId,
|
||||||
|
});
|
||||||
|
this.notify.info(
|
||||||
|
"The starred project changes are now marked as viewed. Click in the list to keep them as new.",
|
||||||
|
TIMEOUTS.LONG,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async markStarredProjectChangesAsReadStartingWith(jwtId: string) {
|
||||||
|
const index = this.newStarredProjectChanges.findIndex(
|
||||||
|
(change) => change.plan.jwtId === jwtId,
|
||||||
|
);
|
||||||
|
if (index !== -1 && index < this.newStarredProjectChanges.length - 1) {
|
||||||
|
// Set to the next change's jwtId
|
||||||
|
await this.$updateSettings({
|
||||||
|
lastAckedStarredProjectChangesJwtId:
|
||||||
|
this.newStarredProjectChanges[index + 1].plan.jwtId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// it's the last entry (or not found), so just keep it the same
|
||||||
|
await this.$updateSettings({
|
||||||
|
lastAckedStarredProjectChangesJwtId:
|
||||||
|
this.lastAckedStarredProjectChangesJwtId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.notify.info(
|
||||||
|
"All starred project changes above that line are marked as unread.",
|
||||||
|
TIMEOUTS.STANDARD,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1527,6 +1527,11 @@ export default class ProjectViewView extends Vue {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!settings.lastAckedStarredProjectChangesJwtId) {
|
||||||
|
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
|
||||||
|
lastAckedStarredProjectChangesJwtId: settings.lastViewedClaimId,
|
||||||
|
});
|
||||||
|
}
|
||||||
this.isStarred = true;
|
this.isStarred = true;
|
||||||
} else {
|
} else {
|
||||||
// Remove from starred projects
|
// Remove from starred projects
|
||||||
|
|||||||
Reference in New Issue
Block a user