forked from jsnbuchanan/crowd-funder-for-time-pwa
- Restore runMigrations functionality for database schema migrations - Remove indexedDBMigrationService.ts (was for IndexedDB to SQLite migration) - Recreate migrationService.ts and db-sql/migration.ts for schema management - Add proper TypeScript error handling with type guards in AccountViewView - Fix CreateAndSubmitClaimResult property access in QuickActionBvcBeginView - Remove LeafletMouseEvent from Vue components array (it's a type, not component) - Add null check for UserNameDialog callback to prevent undefined assignment - Implement extractErrorMessage helper function for consistent error handling - Update router to remove database-migration route The migration system now properly handles database schema evolution across app versions, while the IndexedDB to SQLite migration service has been removed as it was specific to that one-time migration.
341 lines
11 KiB
Vue
341 lines
11 KiB
Vue
<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 -->
|
|
<font-awesome
|
|
icon="chevron-left"
|
|
class="fa-fw text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
|
@click="$router.back()"
|
|
/>
|
|
New Activity For You
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Display a single row with the name of "New Offers To You" with a count. -->
|
|
<div class="flex justify-between" data-testId="showOffersToUser">
|
|
<div>
|
|
<span class="text-lg font-medium"
|
|
>{{ newOffersToUser.length
|
|
}}{{ newOffersToUserHitLimit ? "+" : "" }}</span
|
|
>
|
|
<span class="text-lg font-medium ml-4"
|
|
>New Offer{{ newOffersToUser.length === 1 ? "" : "s" }} To You</span
|
|
>
|
|
<font-awesome
|
|
v-if="newOffersToUser.length > 0"
|
|
:icon="showOffersDetails ? 'chevron-down' : 'chevron-right'"
|
|
class="cursor-pointer ml-4 mr-4 text-lg"
|
|
@click="expandOffersToUserAndMarkRead()"
|
|
/>
|
|
</div>
|
|
<router-link to="/recent-offers-to-user" class="text-blue-500">
|
|
See all
|
|
</router-link>
|
|
</div>
|
|
|
|
<div v-if="showOffersDetails" class="ml-4 mt-4">
|
|
<ul class="list-disc ml-4">
|
|
<li
|
|
v-for="offer in newOffersToUser"
|
|
:key="offer.jwtId"
|
|
class="mt-4 relative group"
|
|
>
|
|
<span>{{
|
|
didInfo(offer.offeredByDid, activeDid, allMyDids, allContacts)
|
|
}}</span>
|
|
offered
|
|
<span v-if="offer.objectDescription">{{
|
|
offer.objectDescription
|
|
}}</span
|
|
>{{ offer.objectDescription && offer.amount ? ", and " : "" }}
|
|
<span v-if="offer.amount">{{
|
|
displayAmount(offer.unit, offer.amount)
|
|
}}</span>
|
|
<router-link
|
|
:to="{ path: '/claim/' + encodeURIComponent(offer.jwtId) }"
|
|
class="text-blue-500"
|
|
>
|
|
<font-awesome
|
|
icon="file-lines"
|
|
class="pl-2 text-blue-500 cursor-pointer"
|
|
/>
|
|
</router-link>
|
|
<!-- New line that appears on hover or when the offer is clicked -->
|
|
<div
|
|
class="absolute left-0 w-full text-left text-gray-500 text-sm hidden group-hover:flex cursor-pointer items-center"
|
|
@click="markOffersAsReadStartingWith(offer.jwtId)"
|
|
>
|
|
<span class="inline-block w-8 h-px bg-gray-500 mr-2" />
|
|
Click to keep all above as new offers
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Display a single row with the name of "New Offers To Your Projects" with a count. -->
|
|
<div
|
|
class="mt-4 flex justify-between"
|
|
data-testId="showOffersToUserProjects"
|
|
>
|
|
<div>
|
|
<span class="text-lg font-medium"
|
|
>{{ newOffersToUserProjects.length
|
|
}}{{ newOffersToUserProjectsHitLimit ? "+" : "" }}</span
|
|
>
|
|
<span class="text-lg font-medium ml-4"
|
|
>New Offer{{ newOffersToUserProjects.length === 1 ? "" : "s" }} To
|
|
Your Projects</span
|
|
>
|
|
<font-awesome
|
|
v-if="newOffersToUserProjects.length > 0"
|
|
:icon="
|
|
showOffersToUserProjectsDetails ? 'chevron-down' : 'chevron-right'
|
|
"
|
|
class="cursor-pointer ml-4 mr-4 text-lg"
|
|
@click="expandOffersToUserProjectsAndMarkRead()"
|
|
/>
|
|
</div>
|
|
<router-link to="/recent-offers-to-user-projects" class="text-blue-500">
|
|
See all
|
|
</router-link>
|
|
</div>
|
|
|
|
<div v-if="showOffersToUserProjectsDetails" class="ml-4 mt-4">
|
|
<ul class="list-disc ml-4">
|
|
<li
|
|
v-for="offer in newOffersToUserProjects"
|
|
:key="offer.jwtId"
|
|
class="mt-4 relative group"
|
|
>
|
|
<span>{{
|
|
didInfo(offer.offeredByDid, activeDid, allMyDids, allContacts)
|
|
}}</span>
|
|
offered
|
|
<span v-if="offer.objectDescription">{{
|
|
offer.objectDescription
|
|
}}</span
|
|
>{{ offer.objectDescription && offer.amount ? ", and " : "" }}
|
|
<span v-if="offer.amount">{{
|
|
displayAmount(offer.unit, offer.amount)
|
|
}}</span>
|
|
to
|
|
<span>{{ offer.planName }}</span>
|
|
<router-link
|
|
:to="{ path: '/claim/' + encodeURIComponent(offer.jwtId) }"
|
|
class="text-blue-500"
|
|
>
|
|
<font-awesome
|
|
icon="file-lines"
|
|
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="markOffersToUserProjectsAsReadStartingWith(offer.jwtId)"
|
|
>
|
|
<span class="inline-block w-8 h-px bg-gray-500 mr-2" />
|
|
Click to keep all above as new offers
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
|
|
import GiftedDialog from "../components/GiftedDialog.vue";
|
|
import QuickNav from "../components/QuickNav.vue";
|
|
import EntityIcon from "../components/EntityIcon.vue";
|
|
import { NotificationIface } from "../constants/app";
|
|
import { Contact } from "../db/tables/contacts";
|
|
import { Router } from "vue-router";
|
|
import {
|
|
didInfo,
|
|
displayAmount,
|
|
getNewOffersToUser,
|
|
getNewOffersToUserProjects,
|
|
} from "../libs/endorserServer";
|
|
import { retrieveAccountDids } from "../libs/util";
|
|
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
|
|
import * as databaseUtil from "../db/databaseUtil";
|
|
import { logger } from "../utils/logger";
|
|
|
|
@Component({
|
|
components: { GiftedDialog, QuickNav, EntityIcon },
|
|
})
|
|
export default class NewActivityView extends Vue {
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
|
$router!: Router;
|
|
activeDid = "";
|
|
allContacts: Array<Contact> = [];
|
|
allMyDids: string[] = [];
|
|
apiServer = "";
|
|
lastAckedOfferToUserJwtId = "";
|
|
lastAckedOfferToUserProjectsJwtId = "";
|
|
newOffersToUser: Array<OfferSummaryRecord> = [];
|
|
newOffersToUserHitLimit = false;
|
|
newOffersToUserProjects: Array<OfferToPlanSummaryRecord> = [];
|
|
newOffersToUserProjectsHitLimit = false;
|
|
|
|
showOffersDetails = false;
|
|
showOffersToUserProjectsDetails = false;
|
|
didInfo = didInfo;
|
|
displayAmount = displayAmount;
|
|
|
|
async created() {
|
|
try {
|
|
const settings = await databaseUtil.retrieveSettingsForActiveAccount();
|
|
this.apiServer = settings.apiServer || "";
|
|
this.activeDid = settings.activeDid || "";
|
|
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || "";
|
|
this.lastAckedOfferToUserProjectsJwtId =
|
|
settings.lastAckedOfferToUserProjectsJwtId || "";
|
|
|
|
const platformService = PlatformServiceFactory.getInstance();
|
|
const queryResult = await platformService.dbQuery(
|
|
"SELECT * FROM contacts",
|
|
);
|
|
this.allContacts = databaseUtil.mapQueryResultToValues(
|
|
queryResult,
|
|
) as unknown as Contact[];
|
|
|
|
this.allMyDids = await retrieveAccountDids();
|
|
|
|
const offersToUserData = await getNewOffersToUser(
|
|
this.axios,
|
|
this.apiServer,
|
|
this.activeDid,
|
|
this.lastAckedOfferToUserJwtId,
|
|
);
|
|
this.newOffersToUser = offersToUserData.data;
|
|
this.newOffersToUserHitLimit = offersToUserData.hitLimit;
|
|
|
|
const offersToUserProjectsData = await getNewOffersToUserProjects(
|
|
this.axios,
|
|
this.apiServer,
|
|
this.activeDid,
|
|
this.lastAckedOfferToUserProjectsJwtId,
|
|
);
|
|
this.newOffersToUserProjects = offersToUserProjectsData.data;
|
|
this.newOffersToUserProjectsHitLimit = offersToUserProjectsData.hitLimit;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
} catch (err: any) {
|
|
logger.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,
|
|
);
|
|
}
|
|
}
|
|
|
|
async expandOffersToUserAndMarkRead() {
|
|
this.showOffersDetails = !this.showOffersDetails;
|
|
if (this.showOffersDetails) {
|
|
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
|
|
lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId,
|
|
});
|
|
// note that we don't update this.lastAckedOfferToUserJwtId in case they
|
|
// later choose the last one to keep the offers as new
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "info",
|
|
title: "Marked as Read",
|
|
text: "The offers are marked as viewed. Click in the list to keep them as new.",
|
|
},
|
|
5000,
|
|
);
|
|
}
|
|
}
|
|
|
|
async markOffersAsReadStartingWith(jwtId: string) {
|
|
const index = this.newOffersToUser.findIndex(
|
|
(offer) => offer.jwtId === jwtId,
|
|
);
|
|
if (index !== -1 && index < this.newOffersToUser.length - 1) {
|
|
// Set to the next offer's jwtId
|
|
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
|
|
lastAckedOfferToUserJwtId: this.newOffersToUser[index + 1].jwtId,
|
|
});
|
|
} else {
|
|
// it's the last entry (or not found), so just keep it the same
|
|
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
|
|
lastAckedOfferToUserJwtId: this.lastAckedOfferToUserJwtId,
|
|
});
|
|
}
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "info",
|
|
title: "Marked as Unread",
|
|
text: "All offers above that line are marked as unread.",
|
|
},
|
|
3000,
|
|
);
|
|
}
|
|
|
|
async expandOffersToUserProjectsAndMarkRead() {
|
|
this.showOffersToUserProjectsDetails =
|
|
!this.showOffersToUserProjectsDetails;
|
|
if (this.showOffersToUserProjectsDetails) {
|
|
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
|
|
lastAckedOfferToUserProjectsJwtId:
|
|
this.newOffersToUserProjects[0].jwtId,
|
|
});
|
|
// note that we don't update this.lastAckedOfferToUserProjectsJwtId in case
|
|
// they later choose the last one to keep the offers as new
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "info",
|
|
title: "Marked as Read",
|
|
text: "The offers are now marked as viewed. Click in the list to keep them as new.",
|
|
},
|
|
5000,
|
|
);
|
|
}
|
|
}
|
|
|
|
async markOffersToUserProjectsAsReadStartingWith(jwtId: string) {
|
|
const index = this.newOffersToUserProjects.findIndex(
|
|
(offer) => offer.jwtId === jwtId,
|
|
);
|
|
if (index !== -1 && index < this.newOffersToUserProjects.length - 1) {
|
|
// Set to the next offer's jwtId
|
|
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
|
|
lastAckedOfferToUserProjectsJwtId:
|
|
this.newOffersToUserProjects[index + 1].jwtId,
|
|
});
|
|
} else {
|
|
// it's the last entry (or not found), so just keep it the same
|
|
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
|
|
lastAckedOfferToUserProjectsJwtId:
|
|
this.lastAckedOfferToUserProjectsJwtId,
|
|
});
|
|
}
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "info",
|
|
title: "Marked as Unread",
|
|
text: "All offers above that line are marked as unread.",
|
|
},
|
|
3000,
|
|
);
|
|
}
|
|
}
|
|
</script>
|