Browse Source

allow blocking another person's content from this user (with iViewContent contact field)

master
Trent Larson 5 days ago
parent
commit
94994a7251
  1. 7
      src/db-sql/migration.ts
  2. 19
      src/db/tables/contacts.ts
  3. 4
      src/libs/fontawesome.ts
  4. 25
      src/libs/util.ts
  5. 120
      src/services/indexedDBMigrationService.ts
  6. 5
      src/views/AccountViewView.vue
  7. 91
      src/views/DIDView.vue
  8. 4
      src/views/DatabaseMigration.vue
  9. 23
      src/views/HomeView.vue

7
src/db-sql/migration.ts

@ -34,7 +34,6 @@ const secretBase64 = arrayBufferToBase64(randomBytes);
const MIGRATIONS = [
{
name: "001_initial",
// see ../db/tables files for explanations of the fields
sql: `
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -119,6 +118,12 @@ const MIGRATIONS = [
);
`,
},
{
name: "002_add_iViewContent_to_contacts",
sql: `
ALTER TABLE contacts ADD COLUMN iViewContent BOOLEAN DEFAULT TRUE;
`,
},
];
/**

19
src/db/tables/contacts.ts

@ -1,15 +1,16 @@
export interface ContactMethod {
export type ContactMethod = {
label: string;
type: string; // eg. "EMAIL", "SMS", "WHATSAPP", maybe someday "GOOGLE-CONTACT-API"
value: string;
}
};
export interface Contact {
export type Contact = {
//
// When adding a property, consider whether it should be added when exporting & sharing contacts.
// When adding a property, consider whether it should be added when exporting & sharing contacts, eg. DataExportSection
did: string;
contactMethods?: Array<ContactMethod>;
iViewContent?: boolean;
name?: string;
nextPubKeyHashB64?: string; // base64-encoded SHA256 hash of next public key
notes?: string;
@ -17,9 +18,15 @@ export interface Contact {
publicKeyBase64?: string;
seesMe?: boolean; // cached value of the server setting
registered?: boolean; // cached value of the server setting
}
};
export type ContactWithJsonStrings = Contact & {
/**
* This is for those cases (eg. with a DB) where every field is a primitive (and not an object).
*
* This is so that we can reuse most of the type and don't have to maintain another copy.
* Another approach uses typescript conditionals: https://chatgpt.com/share/6855cdc3-ab5c-8007-8525-726612016eb2
*/
export type ContactWithJsonStrings = Omit<Contact, "contactMethods"> & {
contactMethods?: string;
};

4
src/libs/fontawesome.ts

@ -10,8 +10,8 @@ import {
faArrowLeft,
faArrowRight,
faArrowRotateBackward,
faArrowUpRightFromSquare,
faArrowUp,
faArrowUpRightFromSquare,
faBan,
faBitcoinSign,
faBurst,
@ -92,8 +92,8 @@ library.add(
faArrowLeft,
faArrowRight,
faArrowRotateBackward,
faArrowUpRightFromSquare,
faArrowUp,
faArrowUpRightFromSquare,
faBan,
faBitcoinSign,
faBurst,

25
src/libs/util.ts

@ -17,7 +17,7 @@ import {
updateDefaultSettings,
} from "../db/index";
import { Account, AccountEncrypted } from "../db/tables/accounts";
import { Contact } from "../db/tables/contacts";
import { Contact, ContactWithJsonStrings } from "../db/tables/contacts";
import * as databaseUtil from "../db/databaseUtil";
import { DEFAULT_PASSKEY_EXPIRATION_MINUTES } from "../db/tables/settings";
import {
@ -974,19 +974,16 @@ export interface DatabaseExport {
*/
export const contactsToExportJson = (contacts: Contact[]): DatabaseExport => {
// Convert each contact to a plain object and ensure all fields are included
const rows = contacts.map((contact) => ({
did: contact.did,
name: contact.name || null,
contactMethods: contact.contactMethods
? JSON.stringify(parseJsonField(contact.contactMethods, []))
: null,
nextPubKeyHashB64: contact.nextPubKeyHashB64 || null,
notes: contact.notes || null,
profileImageUrl: contact.profileImageUrl || null,
publicKeyBase64: contact.publicKeyBase64 || null,
seesMe: contact.seesMe || false,
registered: contact.registered || false,
}));
const rows = contacts.map((contact) => {
const exContact: ContactWithJsonStrings = R.omit(
["contactMethods"],
contact,
);
exContact.contactMethods = contact.contactMethods
? JSON.stringify(contact.contactMethods, [])
: undefined;
return exContact;
});
return {
data: {

120
src/services/indexedDBMigrationService.ts

@ -39,7 +39,6 @@ import {
generateUpdateStatement,
generateInsertStatement,
} from "../db/databaseUtil";
import { updateDefaultSettings } from "../db/databaseUtil";
import { importFromMnemonic } from "../libs/util";
/**
@ -156,11 +155,14 @@ export async function getDexieContacts(): Promise<Contact[]> {
await db.open();
const contacts = await db.contacts.toArray();
logger.info(
`[MigrationService] Retrieved ${contacts.length} contacts from Dexie`,
`[IndexedDBMigrationService] Retrieved ${contacts.length} contacts from Dexie`,
);
return contacts;
} catch (error) {
logger.error("[MigrationService] Error retrieving Dexie contacts:", error);
logger.error(
"[IndexedDBMigrationService] Error retrieving Dexie contacts:",
error,
);
throw new Error(`Failed to retrieve Dexie contacts: ${error}`);
}
}
@ -214,11 +216,14 @@ export async function getSqliteContacts(): Promise<Contact[]> {
}
logger.info(
`[MigrationService] Retrieved ${contacts.length} contacts from SQLite`,
`[IndexedDBMigrationService] Retrieved ${contacts.length} contacts from SQLite`,
);
return contacts;
} catch (error) {
logger.error("[MigrationService] Error retrieving SQLite contacts:", error);
logger.error(
"[IndexedDBMigrationService] Error retrieving SQLite contacts:",
error,
);
throw new Error(`Failed to retrieve SQLite contacts: ${error}`);
}
}
@ -251,11 +256,14 @@ export async function getDexieSettings(): Promise<Settings[]> {
await db.open();
const settings = await db.settings.toArray();
logger.info(
`[MigrationService] Retrieved ${settings.length} settings from Dexie`,
`[IndexedDBMigrationService] Retrieved ${settings.length} settings from Dexie`,
);
return settings;
} catch (error) {
logger.error("[MigrationService] Error retrieving Dexie settings:", error);
logger.error(
"[IndexedDBMigrationService] Error retrieving Dexie settings:",
error,
);
throw new Error(`Failed to retrieve Dexie settings: ${error}`);
}
}
@ -309,11 +317,14 @@ export async function getSqliteSettings(): Promise<Settings[]> {
}
logger.info(
`[MigrationService] Retrieved ${settings.length} settings from SQLite`,
`[IndexedDBMigrationService] Retrieved ${settings.length} settings from SQLite`,
);
return settings;
} catch (error) {
logger.error("[MigrationService] Error retrieving SQLite settings:", error);
logger.error(
"[IndexedDBMigrationService] Error retrieving SQLite settings:",
error,
);
throw new Error(`Failed to retrieve SQLite settings: ${error}`);
}
}
@ -353,11 +364,14 @@ export async function getSqliteAccounts(): Promise<string[]> {
}
logger.info(
`[MigrationService] Retrieved ${dids.length} accounts from SQLite`,
`[IndexedDBMigrationService] Retrieved ${dids.length} accounts from SQLite`,
);
return dids;
} catch (error) {
logger.error("[MigrationService] Error retrieving SQLite accounts:", error);
logger.error(
"[IndexedDBMigrationService] Error retrieving SQLite accounts:",
error,
);
throw new Error(`Failed to retrieve SQLite accounts: ${error}`);
}
}
@ -391,11 +405,14 @@ export async function getDexieAccounts(): Promise<Account[]> {
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
logger.info(
`[MigrationService] Retrieved ${accounts.length} accounts from Dexie`,
`[IndexedDBMigrationService] Retrieved ${accounts.length} accounts from Dexie`,
);
return accounts;
} catch (error) {
logger.error("[MigrationService] Error retrieving Dexie accounts:", error);
logger.error(
"[IndexedDBMigrationService] Error retrieving Dexie accounts:",
error,
);
throw new Error(`Failed to retrieve Dexie accounts: ${error}`);
}
}
@ -429,7 +446,7 @@ export async function getDexieAccounts(): Promise<Account[]> {
* ```
*/
export async function compareDatabases(): Promise<DataComparison> {
logger.info("[MigrationService] Starting database comparison");
logger.info("[IndexedDBMigrationService] Starting database comparison");
const [
dexieContacts,
@ -470,7 +487,7 @@ export async function compareDatabases(): Promise<DataComparison> {
},
};
logger.info("[MigrationService] Database comparison completed", {
logger.info("[IndexedDBMigrationService] Database comparison completed", {
dexieContacts: dexieContacts.length,
sqliteContacts: sqliteContacts.length,
dexieSettings: dexieSettings.length,
@ -679,6 +696,7 @@ function compareAccounts(dexieAccounts: Account[], sqliteDids: string[]) {
* ```
*/
function contactsEqual(contact1: Contact, contact2: Contact): boolean {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const ifEmpty = (arg: any, def: any) => (arg ? arg : def);
const contact1Methods =
contact1.contactMethods &&
@ -954,7 +972,7 @@ export function generateComparisonYaml(comparison: DataComparison): string {
export async function migrateContacts(
overwriteExisting: boolean = false,
): Promise<MigrationResult> {
logger.info("[MigrationService] Starting contact migration", {
logger.info("[IndexedDBMigrationService] Starting contact migration", {
overwriteExisting,
});
@ -990,7 +1008,7 @@ export async function migrateContacts(
);
await platformService.dbExec(sql, params);
result.contactsMigrated++;
logger.info(`[MigrationService] Updated contact: ${contact.did}`);
logger.info(`[IndexedDBMigrationService] Updated contact: ${contact.did}`);
} else {
result.warnings.push(
`Contact ${contact.did} already exists, skipping`,
@ -1004,17 +1022,17 @@ export async function migrateContacts(
);
await platformService.dbExec(sql, params);
result.contactsMigrated++;
logger.info(`[MigrationService] Added contact: ${contact.did}`);
logger.info(`[IndexedDBMigrationService] Added contact: ${contact.did}`);
}
} catch (error) {
const errorMsg = `Failed to migrate contact ${contact.did}: ${error}`;
logger.error("[MigrationService]", errorMsg);
logger.error("[IndexedDBMigrationService]", errorMsg);
result.errors.push(errorMsg);
result.success = false;
}
}
logger.info("[MigrationService] Contact migration completed", {
logger.info("[IndexedDBMigrationService] Contact migration completed", {
contactsMigrated: result.contactsMigrated,
errors: result.errors.length,
warnings: result.warnings.length,
@ -1023,7 +1041,7 @@ export async function migrateContacts(
return result;
} catch (error) {
const errorMsg = `Contact migration failed: ${error}`;
logger.error("[MigrationService]", errorMsg);
logger.error("[IndexedDBMigrationService]", errorMsg);
result.errors.push(errorMsg);
result.success = false;
return result;
@ -1063,7 +1081,7 @@ export async function migrateContacts(
* ```
*/
export async function migrateSettings(): Promise<MigrationResult> {
logger.info("[MigrationService] Starting settings migration");
logger.info("[IndexedDBMigrationService] Starting settings migration");
const result: MigrationResult = {
success: true,
@ -1076,17 +1094,17 @@ export async function migrateSettings(): Promise<MigrationResult> {
try {
const dexieSettings = await getDexieSettings();
logger.info("[MigrationService] Migrating settings", {
logger.info("[IndexedDBMigrationService] Migrating settings", {
dexieSettings: dexieSettings.length,
});
const platformService = PlatformServiceFactory.getInstance();
// Create an array of promises for all settings migrations
const migrationPromises = dexieSettings.map(async (setting) => {
logger.info("[MigrationService] Starting to migrate settings", setting);
let sqliteSettingRaw:
| { columns: string[]; values: unknown[][] }
| undefined;
logger.info(
"[IndexedDBMigrationService] Starting to migrate settings",
setting,
);
// adjust SQL based on the accountDid key, maybe null
let conditional: string;
@ -1098,15 +1116,18 @@ export async function migrateSettings(): Promise<MigrationResult> {
conditional = "accountDid = ?";
preparams = [setting.accountDid];
}
sqliteSettingRaw = await platformService.dbQuery(
const sqliteSettingRaw = await platformService.dbQuery(
"SELECT * FROM settings WHERE " + conditional,
preparams,
);
logger.info("[MigrationService] Migrating one set of settings:", {
logger.info(
"[IndexedDBMigrationService] Migrating one set of settings:",
{
setting,
sqliteSettingRaw,
});
},
);
if (sqliteSettingRaw?.values?.length) {
// should cover the master settings, where accountDid is null
delete setting.id; // don't conflict with the id in the sqlite database
@ -1117,7 +1138,7 @@ export async function migrateSettings(): Promise<MigrationResult> {
conditional,
preparams,
);
logger.info("[MigrationService] Updating settings", {
logger.info("[IndexedDBMigrationService] Updating settings", {
sql,
params,
});
@ -1127,10 +1148,7 @@ export async function migrateSettings(): Promise<MigrationResult> {
// insert new setting
delete setting.id; // don't conflict with the id in the sqlite database
delete setting.activeDid; // ensure we don't set the activeDid (since master settings are an update and don't hit this case)
const { sql, params } = generateInsertStatement(
setting,
"settings",
);
const { sql, params } = generateInsertStatement(setting, "settings");
await platformService.dbExec(sql, params);
result.settingsMigrated++;
}
@ -1140,7 +1158,7 @@ export async function migrateSettings(): Promise<MigrationResult> {
const updatedSettings = await Promise.all(migrationPromises);
logger.info(
"[MigrationService] Finished migrating settings",
"[IndexedDBMigrationService] Finished migrating settings",
updatedSettings,
result,
);
@ -1148,7 +1166,7 @@ export async function migrateSettings(): Promise<MigrationResult> {
return result;
} catch (error) {
logger.error(
"[MigrationService] Complete settings migration failed:",
"[IndexedDBMigrationService] Complete settings migration failed:",
error,
);
const errorMessage = `Settings migration failed: ${error}`;
@ -1192,7 +1210,7 @@ export async function migrateSettings(): Promise<MigrationResult> {
* ```
*/
export async function migrateAccounts(): Promise<MigrationResult> {
logger.info("[MigrationService] Starting account migration");
logger.info("[IndexedDBMigrationService] Starting account migration");
const result: MigrationResult = {
success: true,
@ -1248,14 +1266,17 @@ export async function migrateAccounts(): Promise<MigrationResult> {
);
}
logger.info("[MigrationService] Successfully migrated account", {
logger.info(
"[IndexedDBMigrationService] Successfully migrated account",
{
did,
dateCreated: account.dateCreated,
});
},
);
} catch (error) {
const errorMessage = `Failed to migrate account ${did}: ${error}`;
result.errors.push(errorMessage);
logger.error("[MigrationService] Account migration failed:", {
logger.error("[IndexedDBMigrationService] Account migration failed:", {
error,
did,
});
@ -1272,7 +1293,7 @@ export async function migrateAccounts(): Promise<MigrationResult> {
result.errors.push(errorMessage);
result.success = false;
logger.error(
"[MigrationService] Complete account migration failed:",
"[IndexedDBMigrationService] Complete account migration failed:",
error,
);
return result;
@ -1306,11 +1327,11 @@ export async function migrateAll(): Promise<MigrationResult> {
try {
logger.info(
"[MigrationService] Starting complete migration from Dexie to SQLite",
"[IndexedDBMigrationService] Starting complete migration from Dexie to SQLite",
);
// Step 1: Migrate Accounts (foundational)
logger.info("[MigrationService] Step 1: Migrating accounts...");
logger.info("[IndexedDBMigrationService] Step 1: Migrating accounts...");
const accountsResult = await migrateAccounts();
if (!accountsResult.success) {
result.errors.push(
@ -1322,7 +1343,7 @@ export async function migrateAll(): Promise<MigrationResult> {
result.warnings.push(...accountsResult.warnings);
// Step 2: Migrate Settings (depends on accounts)
logger.info("[MigrationService] Step 2: Migrating settings...");
logger.info("[IndexedDBMigrationService] Step 2: Migrating settings...");
const settingsResult = await migrateSettings();
if (!settingsResult.success) {
result.errors.push(
@ -1335,7 +1356,7 @@ export async function migrateAll(): Promise<MigrationResult> {
// Step 4: Migrate Contacts (independent, but after accounts for consistency)
// ... but which is better done through the contact import view
// logger.info("[MigrationService] Step 4: Migrating contacts...");
// logger.info("[IndexedDBMigrationService] Step 4: Migrating contacts...");
// const contactsResult = await migrateContacts();
// if (!contactsResult.success) {
// result.errors.push(
@ -1354,7 +1375,7 @@ export async function migrateAll(): Promise<MigrationResult> {
result.contactsMigrated;
logger.info(
`[MigrationService] Complete migration successful: ${totalMigrated} total records migrated`,
`[IndexedDBMigrationService] Complete migration successful: ${totalMigrated} total records migrated`,
{
accounts: result.accountsMigrated,
settings: result.settingsMigrated,
@ -1367,7 +1388,10 @@ export async function migrateAll(): Promise<MigrationResult> {
} catch (error) {
const errorMessage = `Complete migration failed: ${error}`;
result.errors.push(errorMessage);
logger.error("[MigrationService] Complete migration failed:", error);
logger.error(
"[IndexedDBMigrationService] Complete migration failed:",
error,
);
return result;
}
}

5
src/views/AccountViewView.vue

@ -349,8 +349,9 @@
</div>
<div v-if="includeUserProfileLocation" class="mb-4 aspect-video">
<p class="text-sm mb-2 text-slate-500">
The location you choose will be shared with the world until you remove this checkbox.
For your security, choose a location nearby but not exactly at your true location, like at your town center.
The location you choose will be shared with the world until you remove
this checkbox. For your security, choose a location nearby but not
exactly at your true location, like at your town center.
</p>
<l-map

91
src/views/DIDView.vue

@ -77,6 +77,7 @@
@click="confirmSetVisibility(contactFromDid, false)"
>
<font-awesome icon="eye" class="fa-fw" />
<font-awesome icon="arrow-up" class="fa-fw" />
</button>
<button
v-else-if="
@ -87,6 +88,32 @@
@click="confirmSetVisibility(contactFromDid, true)"
>
<font-awesome icon="eye-slash" class="fa-fw" />
<font-awesome icon="arrow-up" class="fa-fw" />
</button>
<button
v-if="
contactFromDid?.iViewContent &&
contactFromDid.did !== activeDid
"
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
title="I view their content"
@click="confirmViewContent(contactFromDid, false)"
>
<font-awesome icon="eye" class="fa-fw" />
<font-awesome icon="arrow-down" class="fa-fw" />
</button>
<button
v-else-if="
!contactFromDid?.iViewContent &&
contactFromDid?.did !== activeDid
"
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
title="I do not view their content"
@click="confirmViewContent(contactFromDid, true)"
>
<font-awesome icon="eye-slash" class="fa-fw" />
<font-awesome icon="arrow-down" class="fa-fw" />
</button>
<button
@ -825,9 +852,9 @@ export default class DIDView extends Vue {
title: "Visibility Refreshed",
text:
libsUtil.nameForContact(contact, true) +
" can " +
(visibility ? "" : "not ") +
"see your activity.",
" can" +
(visibility ? "" : " not") +
" see your activity.",
},
3000,
);
@ -857,6 +884,64 @@ export default class DIDView extends Vue {
);
}
}
/**
* Confirm whether the user want to see/hide the other's content, then execute it
*
* @param contact Contact content to show/hide from user
* @param view whether user wants to view this contact
*/
async confirmViewContent(contact: Contact, view: boolean) {
const contentVisibilityPrompt = view
? "Are you sure you want to see their content?"
: "Are you sure you want to hide their content from you?";
this.$notify(
{
group: "modal",
type: "confirm",
title: "Set Content Visibility",
text: contentVisibilityPrompt,
onYes: async () => {
const success = await this.setViewContent(contact, view);
if (success) {
contact.iViewContent = view; // see visibility note about not working inside setVisibility
}
},
},
-1,
);
}
/**
* Updates contact content visibility for this device
*
* @param contact - Contact to update content visibility for
* @param visibility - New content visibility state
* @returns Boolean indicating success
*/
async setViewContent(contact: Contact, visibility: boolean) {
const platformService = PlatformServiceFactory.getInstance();
await platformService.dbExec(
"UPDATE contacts SET iViewContent = ? WHERE did = ?",
[visibility, contact.did],
);
if (USE_DEXIE_DB) {
db.contacts.update(contact.did, { iViewContent: visibility });
}
this.$notify(
{
group: "alert",
type: "success",
title: "Visibility Set",
text:
"You will" +
(visibility ? "" : " not") +
` see ${contact.name}'s activity.`,
},
3000,
);
return true;
}
}
</script>

4
src/views/DatabaseMigration.vue

@ -1122,6 +1122,7 @@ export default class DatabaseMigration extends Vue {
private loadingMessage = "";
private error = "";
private warning = "";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private exportedData: Record<string, any> | null = null;
private successMessage = "";
@ -1134,6 +1135,7 @@ export default class DatabaseMigration extends Vue {
* @param {any} setting - The setting object
* @returns {string} The display name for the setting
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getSettingDisplayName(setting: any): string {
// Handle exported JSON format (has 'type' and 'did' fields)
if (setting.type && setting.did) {
@ -1153,6 +1155,7 @@ export default class DatabaseMigration extends Vue {
* @param {any} account - The account object
* @returns {boolean} True if account has identity
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getAccountHasIdentity(account: any): boolean {
// Handle exported JSON format (has 'hasIdentity' field)
if (account.hasIdentity !== undefined) {
@ -1170,6 +1173,7 @@ export default class DatabaseMigration extends Vue {
* @param {any} account - The account object
* @returns {boolean} True if account has mnemonic
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getAccountHasMnemonic(account: any): boolean {
// Handle exported JSON format (has 'hasMnemonic' field)
if (account.hasMnemonic !== undefined) {

23
src/views/HomeView.vue

@ -448,6 +448,7 @@ export default class HomeView extends Vue {
allContacts: Array<Contact> = [];
allMyDids: Array<string> = [];
apiServer = "";
blockedContactDids: Array<string> = [];
feedData: GiveRecordWithContactInfo[] = [];
feedPreviousOldestId?: string;
feedLastViewedClaimId?: string;
@ -567,22 +568,14 @@ export default class HomeView extends Vue {
// Load contacts with graceful fallback
try {
const platformService = PlatformServiceFactory.getInstance();
const dbContacts = await platformService.dbQuery(
"SELECT * FROM contacts",
);
this.allContacts = databaseUtil.mapQueryResultToValues(
dbContacts,
) as Contact[];
if (USE_DEXIE_DB) {
this.allContacts = await db.contacts.toArray();
}
this.loadContacts();
} catch (error) {
logConsoleAndDb(
`[HomeView] Failed to retrieve contacts: ${error}`,
true,
);
this.allContacts = []; // Ensure we have a valid empty array
this.blockedContactDids = [];
this.$notify(
{
group: "alert",
@ -746,6 +739,9 @@ export default class HomeView extends Vue {
if (USE_DEXIE_DB) {
this.allContacts = await db.contacts.toArray();
}
this.blockedContactDids = this.allContacts
.filter((c) => !c.iViewContent)
.map((c) => c.did);
}
/**
@ -1013,6 +1009,7 @@ export default class HomeView extends Vue {
);
if (results.data.length > 0) {
endOfResults = false;
// gather any contacts that user has blocked from view
await this.processFeedResults(results.data);
await this.updateFeedLastViewedId(results.data);
}
@ -1200,7 +1197,7 @@ export default class HomeView extends Vue {
}
/**
* Checks if record should be included based on filters
* Checks if record should be included based on filters & preferences
*
* @internal
* @callGraph
@ -1226,6 +1223,10 @@ export default class HomeView extends Vue {
record: GiveSummaryRecord,
fulfillsPlan?: FulfillsPlan,
): boolean {
if (this.blockedContactDids.includes(record.issuerDid)) {
return false;
}
if (!this.isAnyFeedFilterOn) {
return true;
}

Loading…
Cancel
Save