add large notice when user has a new offer to them
This commit is contained in:
@@ -207,9 +207,9 @@ export default class OfferDialog extends Vue {
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: "You must select an identifier before you can record an offer.",
|
||||
text: "You must select an identity before you can record an offer.",
|
||||
},
|
||||
-1,
|
||||
7000,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -264,7 +264,7 @@ export default class OfferDialog extends Vue {
|
||||
title: "Success",
|
||||
text: "That offer was recorded.",
|
||||
},
|
||||
10000,
|
||||
5000,
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
@@ -31,8 +31,13 @@ export type Settings = {
|
||||
isRegistered?: boolean;
|
||||
imageServer?: string;
|
||||
lastName?: string; // deprecated - put all names in firstName
|
||||
|
||||
lastAckedOfferToUserJwtId?: string; // the last JWT ID for offer-to-user that they've acknowledged
|
||||
|
||||
// The claim list has a most recent one used in notifications that's separate from the last viewed
|
||||
lastNotifiedClaimId?: string;
|
||||
lastViewedClaimId?: string;
|
||||
|
||||
passkeyExpirationMinutes?: number; // passkey access token time-to-live in minutes
|
||||
profileImageUrl?: string; // may be null if unwanted for a particular account
|
||||
reminderTime?: number; // Time in milliseconds since UNIX epoch for reminders
|
||||
@@ -53,7 +58,7 @@ export type Settings = {
|
||||
webPushServer?: string; // Web Push server URL
|
||||
};
|
||||
|
||||
export function isAnyFeedFilterOn(settings: Settings): boolean {
|
||||
export function checkIsAnyFeedFilterOn(settings: Settings): boolean {
|
||||
return !!(settings?.filterFeedByNearby || settings?.filterFeedByVisible);
|
||||
}
|
||||
|
||||
|
||||
@@ -587,6 +587,17 @@ export async function setPlanInCache(
|
||||
planCache.set(handleId, planSummary);
|
||||
}
|
||||
|
||||
export async function getNewOffersToUser(axios: Axios, apiServer: string, activeDid: string, lastAckedOfferToUserJwtId?: string) {
|
||||
let url = `${apiServer}/api/v2/report/offers?recipientDid=${activeDid}`;
|
||||
if (lastAckedOfferToUserJwtId) {
|
||||
url += "&afterId=" + lastAckedOfferToUserJwtId;
|
||||
}
|
||||
const headers = await getHeaders(activeDid);
|
||||
const response = await axios.get(url, { headers });
|
||||
const offers = response.data.data;
|
||||
return offers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct GiveAction VC for submission to server
|
||||
*
|
||||
|
||||
@@ -133,6 +133,11 @@ const routes: Array<RouteRecordRaw> = [
|
||||
name: "invite-one",
|
||||
component: () => import("../views/InviteOneView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/new-activity",
|
||||
name: "new-activity",
|
||||
component: () => import("../views/NewActivityView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/new-edit-account",
|
||||
name: "new-edit-account",
|
||||
|
||||
@@ -123,7 +123,7 @@ export default class ContactGiftingView extends Vue {
|
||||
err.message ||
|
||||
"There was an error retrieving your settings or contacts.",
|
||||
},
|
||||
-1,
|
||||
5000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
@click="toggleShowContactAmounts()"
|
||||
>
|
||||
{{
|
||||
showGiveNumbers ? "Hide Given Hours etc" : "Show Given Hours etc"
|
||||
showGiveNumbers ? "Hide Given Hours, etc" : "Show Given Hours, etc"
|
||||
}}
|
||||
</button>
|
||||
</div>
|
||||
@@ -182,6 +182,25 @@
|
||||
>
|
||||
<button
|
||||
class="text-sm bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-1.5 rounded-l-md"
|
||||
@click="confirmShowGiftedDialog(contact.did, activeDid)"
|
||||
:title="givenToMeDescriptions[contact.did] || ''"
|
||||
>
|
||||
From:
|
||||
<br />
|
||||
{{
|
||||
/* eslint-disable prettier/prettier */
|
||||
this.showGiveTotals
|
||||
? ((givenToMeConfirmed[contact.did] || 0)
|
||||
+ (givenToMeUnconfirmed[contact.did] || 0))
|
||||
: this.showGiveConfirmed
|
||||
? (givenToMeConfirmed[contact.did] || 0)
|
||||
: (givenToMeUnconfirmed[contact.did] || 0)
|
||||
/* eslint-enable prettier/prettier */
|
||||
}}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="text-sm bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white -ml-1.5 px-2 py-1.5 rounded-r-md border-l"
|
||||
@click="confirmShowGiftedDialog(activeDid, contact.did)"
|
||||
:title="givenByMeDescriptions[contact.did] || ''"
|
||||
>
|
||||
@@ -197,29 +216,6 @@
|
||||
: (givenByMeUnconfirmed[contact.did] || 0)
|
||||
/* eslint-enable prettier/prettier */
|
||||
}}
|
||||
<br />
|
||||
<fa icon="plus" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="text-sm bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white -ml-1.5 px-2 py-1.5 rounded-r-md border-l"
|
||||
@click="confirmShowGiftedDialog(contact.did, this.activeDid)"
|
||||
:title="givenToMeDescriptions[contact.did] || ''"
|
||||
>
|
||||
From:
|
||||
<br />
|
||||
{{
|
||||
/* eslint-disable prettier/prettier */
|
||||
this.showGiveTotals
|
||||
? ((givenToMeConfirmed[contact.did] || 0)
|
||||
+ (givenToMeUnconfirmed[contact.did] || 0))
|
||||
: this.showGiveConfirmed
|
||||
? (givenToMeConfirmed[contact.did] || 0)
|
||||
: (givenToMeUnconfirmed[contact.did] || 0)
|
||||
/* eslint-enable prettier/prettier */
|
||||
}}
|
||||
<br />
|
||||
<fa icon="plus" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -587,7 +583,7 @@ export default class ContactsView extends Vue {
|
||||
(useRecipient ? "given" : "received") +
|
||||
" data from the server.",
|
||||
},
|
||||
-1,
|
||||
5000,
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1167,7 +1163,7 @@ export default class ContactsView extends Vue {
|
||||
title: "Error Updating Contact Setting",
|
||||
text: "The setting may not have saved. Try again, maybe after restarting the app.",
|
||||
},
|
||||
-1,
|
||||
5000,
|
||||
);
|
||||
console.error(
|
||||
"Telling user to try again after contact-amounts setting update because:",
|
||||
|
||||
@@ -196,6 +196,32 @@
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="numNewOffersToUser"
|
||||
@click="goToActivityToUserPage"
|
||||
class="border-t p-2 border-slate-300"
|
||||
>
|
||||
<div class="flex justify-center">
|
||||
<div
|
||||
v-if="numNewOffersToUser"
|
||||
class="bg-gradient-to-b from-green-400 to-green-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] px-4 py-4 rounded-md text-white"
|
||||
>
|
||||
<span class="block text-center text-6xl">
|
||||
{{ numNewOffersToUser }}
|
||||
</span>
|
||||
<p>
|
||||
new offer{{ numNewOffersToUser === 1 ? "" : "s" }} to you
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end mt-2">
|
||||
<button class="text-blue-500">
|
||||
View All New Activity For You
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InfiniteScroll @reached-bottom="loadMoreGives">
|
||||
<ul id="listLatestActivity" class="border-t border-slate-300">
|
||||
<li
|
||||
@@ -204,7 +230,7 @@
|
||||
:key="record.jwtId"
|
||||
>
|
||||
<div
|
||||
class="border-b border-dashed border-slate-400 text-orange-400 pb-2 mb-2 font-bold text-sm"
|
||||
class="border-b border-slate-300 text-orange-400 pb-2 mb-2 font-bold text-sm"
|
||||
v-if="record.jwtId == feedLastViewedClaimId"
|
||||
>
|
||||
You've already seen all the following
|
||||
@@ -345,7 +371,7 @@ import {
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import {
|
||||
BoundingBox,
|
||||
isAnyFeedFilterOn,
|
||||
checkIsAnyFeedFilterOn,
|
||||
MASTER_SETTINGS_KEY,
|
||||
} from "@/db/tables/settings";
|
||||
import {
|
||||
@@ -354,6 +380,7 @@ import {
|
||||
didInfoForContact,
|
||||
fetchEndorserRateLimits,
|
||||
getHeaders,
|
||||
getNewOffersToUser,
|
||||
getPlanFromCache,
|
||||
GiveSummaryRecord,
|
||||
} from "@/libs/endorserServer";
|
||||
@@ -418,6 +445,8 @@ export default class HomeView extends Vue {
|
||||
isFeedFilteredByNearby = false;
|
||||
isFeedLoading = true;
|
||||
isRegistered = false;
|
||||
lastAckedOfferToUserJwtId?: string; // the last JWT ID for offer-to-user that they've acknowledged
|
||||
numNewOffersToUser: number = 0; // number of new offers-to-user
|
||||
searchBoxes: Array<{
|
||||
name: string;
|
||||
bbox: BoundingBox;
|
||||
@@ -447,10 +476,11 @@ export default class HomeView extends Vue {
|
||||
this.isFeedFilteredByVisible = !!settings.filterFeedByVisible;
|
||||
this.isFeedFilteredByNearby = !!settings.filterFeedByNearby;
|
||||
this.isRegistered = !!settings.isRegistered;
|
||||
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId;
|
||||
this.searchBoxes = settings.searchBoxes || [];
|
||||
this.showShortcutBvc = !!settings.showShortcutBvc;
|
||||
|
||||
this.isAnyFeedFilterOn = isAnyFeedFilterOn(settings);
|
||||
this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings);
|
||||
|
||||
if (!settings.finishedOnboarding) {
|
||||
(this.$refs.onboardingDialog as OnboardingDialog).open(
|
||||
@@ -480,6 +510,12 @@ export default class HomeView extends Vue {
|
||||
// this returns a Promise but we don't need to wait for it
|
||||
this.updateAllFeed();
|
||||
|
||||
if (this.activeDid) {
|
||||
this.numNewOffersToUser =
|
||||
(await getNewOffersToUser(this.axios, this.apiServer, this.activeDid, this.lastAckedOfferToUserJwtId))
|
||||
.length;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
console.error("Error retrieving settings or feed.", err);
|
||||
@@ -519,7 +555,7 @@ export default class HomeView extends Vue {
|
||||
const settings = await retrieveSettingsForActiveAccount();
|
||||
this.isFeedFilteredByVisible = !!settings.filterFeedByVisible;
|
||||
this.isFeedFilteredByNearby = !!settings.filterFeedByNearby;
|
||||
this.isAnyFeedFilterOn = isAnyFeedFilterOn(settings);
|
||||
this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings);
|
||||
|
||||
this.feedData = [];
|
||||
this.feedPreviousOldestId = undefined;
|
||||
@@ -782,6 +818,10 @@ export default class HomeView extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
goToActivityToUserPage() {
|
||||
(this.$router as Router).push({ name: "new-activity" });
|
||||
}
|
||||
|
||||
onClickLoadClaim(jwtId: string) {
|
||||
const route = {
|
||||
path: "/claim/" + encodeURIComponent(jwtId),
|
||||
@@ -820,7 +860,7 @@ export default class HomeView extends Vue {
|
||||
(this.$refs.feedFilters as FeedFilters).open(this.reloadFeedOnChange);
|
||||
}
|
||||
|
||||
toastUser(message) {
|
||||
toastUser(message: string) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
|
||||
111
src/views/NewActivityView.vue
Normal file
111
src/views/NewActivityView.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<QuickNav selected="Home"></QuickNav>
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||
<!-- Breadcrumb -->
|
||||
<div id="ViewBreadcrumb" class="mb-8">
|
||||
<h1 class="text-lg text-center font-light relative px-7" >
|
||||
<!-- Back -->
|
||||
<fa
|
||||
icon="chevron-left"
|
||||
@click="$router.back()"
|
||||
class="fa-fw text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
||||
/>
|
||||
New Activity For You
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!-- Display a single row with the name of "New Offers To You" with a count. -->
|
||||
<div class="new-offers-row flex justify-between items-center mb-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-lg font-medium">New Offers To You</span>
|
||||
<fa
|
||||
:icon="showOffersDetails ? 'chevron-down' : 'chevron-right'"
|
||||
class="cursor-pointer ml-4"
|
||||
@click="showOffersDetails = !showOffersDetails"
|
||||
/>
|
||||
</div>
|
||||
<span class="text-lg font-medium">{{ newOffersToUser.length }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="showOffersDetails" class="ml-4">
|
||||
<ul>
|
||||
<li v-for="offer in newOffersToUser" :key="offer.id" class="mt-2">
|
||||
<span>{{ didInfo(offer.offeredByDid, activeDid, allMyDids, allContacts) }}</span>
|
||||
offers
|
||||
<span v-if="offer.description">{{ offer.description }}</span>
|
||||
<span v-if="offer.amount">{{ displayAmount(offer.unit, offer.amount) }}</span>
|
||||
<router-link :to="{ path: '/claim/' + encodeURIComponent(offer.jwtId) }" class="text-blue-500">
|
||||
<fa icon="file-lines" class="pl-2 text-blue-500 cursor-pointer" />
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { Router } from "vue-router";
|
||||
|
||||
import GiftedDialog from "@/components/GiftedDialog.vue";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import EntityIcon from "@/components/EntityIcon.vue";
|
||||
import { NotificationIface } from "@/constants/app";
|
||||
import { accountsDB, db, retrieveSettingsForActiveAccount } from "@/db/index";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import { didInfo,displayAmount, getNewOffersToUser, OfferSummaryRecord } from "@/libs/endorserServer";
|
||||
|
||||
@Component({
|
||||
components: { GiftedDialog, QuickNav, EntityIcon },
|
||||
})
|
||||
export default class NewActivityView extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
|
||||
activeDid = "";
|
||||
allContacts: Array<Contact> = [];
|
||||
allMyDids: string[] = [];
|
||||
apiServer = "";
|
||||
lastAckedOfferToUserJwtId = "";
|
||||
newOffersToUser: Array<OfferSummaryRecord> = [];
|
||||
|
||||
showOffersDetails = false;
|
||||
|
||||
didInfo = didInfo;
|
||||
displayAmount = displayAmount;
|
||||
|
||||
async created() {
|
||||
try {
|
||||
const settings = await retrieveSettingsForActiveAccount();
|
||||
this.apiServer = settings.apiServer || "";
|
||||
this.activeDid = settings.activeDid || "";
|
||||
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || "";
|
||||
|
||||
this.allContacts = await db.contacts.toArray();
|
||||
|
||||
await accountsDB.open();
|
||||
const allAccounts = await accountsDB.accounts.toArray();
|
||||
if (allAccounts.length > 0) {
|
||||
this.allMyDids = allAccounts.map((acc) => acc.did);
|
||||
}
|
||||
this.newOffersToUser =
|
||||
await getNewOffersToUser(this.axios, this.apiServer, this.activeDid, this.lastAckedOfferToUserJwtId);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
console.error("Error retrieving settings & contacts:", err);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text:
|
||||
err.message ||
|
||||
"There was an error retrieving your activity.",
|
||||
},
|
||||
5000,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user