Compare commits

...

8 Commits

Author SHA1 Message Date
Jose Olarte III
3544d7278d Optimized item actions
- Edited button labels for brevity
- Repositioned Totals toggle
- Restyled note about recent hours
- Various text size and spacing changes
2025-06-10 19:54:05 +08:00
Jose Olarte III
d3110506ea Optimized per-item layout
- Stacked contact name and DID
- Text truncates to leave room for action buttons when visible
- Separated "from / to" heading from buttons to minimize width
- Various spacing and alignment adjustments
2025-06-10 18:42:49 +08:00
8609f8458d bump to build 25 & version 0.5.0 2025-06-09 09:26:21 -06:00
8f5c34bc5f fix linting 2025-06-09 09:09:54 -06:00
b0d61b95ea Merge branch 'ui-fixes-2025-06-w2' 2025-06-09 08:44:42 -06:00
af7bd236a3 fix check for successful gift submission 2025-06-09 08:41:47 -06:00
d719338bcc fix problem setting 'loading' flag 2025-06-09 08:37:42 -06:00
6ddf2d1012 fix problem switching IDs (creating too many settings) 2025-06-09 08:33:33 -06:00
18 changed files with 255 additions and 266 deletions

View File

@@ -9,19 +9,6 @@ For a quick dev environment setup, use [pkgx](https://pkgx.dev).
- Node.js (LTS version recommended)
- npm (comes with Node.js)
- Git
- For Android builds: Android Studio with SDK installed
- For iOS builds: macOS with Xcode and ruby gems & bundle
- `pkgx +rubygems.org sh`
- ... and you may have to fix these, especially with pkgx
```bash
gem_path=$(which gem)
shortened_path="${gem_path:h:h}"
export GEM_HOME=$shortened_path
export GEM_PATH=$shortened_path
```
- For desktop builds: Additional build tools based on your OS
## Forks
@@ -326,6 +313,32 @@ npm run build:electron-prod && npm run electron:start
Prerequisites: macOS with Xcode installed
#### First-time iOS Configuration
- Generate certificates inside XCode.
- Right-click on App and under Signing & Capabilities set the Team.
#### Each Release
0. First time (or if XCode dependencies change):
- `pkgx +rubygems.org sh`
- ... and you may have to fix these, especially with pkgx
```bash
gem_path=$(which gem)
shortened_path="${gem_path:h:h}"
export GEM_HOME=$shortened_path
export GEM_PATH=$shortened_path
```
```bash
cd ios/App
pod install
```
1. Build the web assets:
```bash
@@ -334,6 +347,7 @@ Prerequisites: macOS with Xcode installed
npm run build:capacitor
```
2. Update iOS project with latest build:
```bash
@@ -357,10 +371,10 @@ Prerequisites: macOS with Xcode installed
```
cd ios/App
xcrun agvtool new-version 21
xcrun agvtool new-version 25
# Unfortunately this edits Info.plist directly.
#xcrun agvtool new-marketing-version 0.4.5
cat App.xcodeproj/project.pbxproj | sed "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 0.4.7;/g" > temp
cat App.xcodeproj/project.pbxproj | sed "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 0.5.0;/g" > temp
mv temp App.xcodeproj/project.pbxproj
cd -
```
@@ -377,7 +391,7 @@ Prerequisites: macOS with Xcode installed
7. Release
* Under "General" we want to rename a bunch of things to "Time Safari"
* Someday: Under "General" we want to rename a bunch of things to "Time Safari"
* Choose Product -> Destination -> Any iOS Device
* Choose Product -> Archive
* This will trigger a build and take time, needing user's "login" keychain password (user's login password), repeatedly.
@@ -389,15 +403,9 @@ Prerequisites: macOS with Xcode installed
* You'll probably have to "Manage" something about encryption, disallowed in France.
* Then "Save" and "Add to Review" and "Resubmit to App Review".
#### First-time iOS Configuration
- Generate certificates inside XCode.
- Right-click on App and under Signing & Capabilities set the Team.
### Android Build
Prerequisites: Android Studio with SDK installed
Prerequisites: Android Studio with Java SDK installed
1. Build the web assets:
@@ -452,7 +460,9 @@ Prerequisites: Android Studio with SDK installed
* Then `bundleRelease`:
```bash
cd android
./gradlew bundleRelease -Dlint.baselines.continue=true
cd -
```
... and find your `aab` file at app/build/outputs/bundle/release

View File

@@ -31,8 +31,8 @@ android {
applicationId "app.timesafari.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 23
versionName "0.4.8"
versionCode 25
versionName "0.5.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

View File

@@ -32,7 +32,7 @@
}
},
"ios": {
"contentInset": "always",
"contentInset": "never",
"allowsLinkPreview": true,
"scrollEnabled": true,
"limitsNavigationsToAppBoundDomains": true,

View File

@@ -403,7 +403,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 23;
CURRENT_PROJECT_VERSION = 25;
DEVELOPMENT_TEAM = GM3FS5JQPH;
ENABLE_APP_SANDBOX = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
@@ -413,7 +413,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.4.8;
MARKETING_VERSION = 0.5.0;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -430,7 +430,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 23;
CURRENT_PROJECT_VERSION = 25;
DEVELOPMENT_TEAM = GM3FS5JQPH;
ENABLE_APP_SANDBOX = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
@@ -440,7 +440,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.4.8;
MARKETING_VERSION = 0.5.0;
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";

View File

@@ -49,7 +49,11 @@
</div>
</div>
<a class="cursor-pointer" @click="$emit('loadClaim', record.jwtId)" data-testid="circle-info-link">
<a
class="cursor-pointer"
data-testid="circle-info-link"
@click="$emit('loadClaim', record.jwtId)"
>
<font-awesome icon="circle-info" class="fa-fw text-slate-500" />
</a>
</div>

View File

@@ -48,10 +48,7 @@
<span>
{{ didInfo(visDid) }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(visDid)">
<a
:href="`/did/${visDid}`"
class="text-blue-500"
>
<a :href="`/did/${visDid}`" class="text-blue-500">
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"

View File

@@ -14,8 +14,8 @@
</div>
</h1>
The feed underneath this pop-up shows the latest contributions,
some from people and some from projects.
The feed underneath this pop-up shows the latest contributions, some from
people and some from projects.
<p v-if="isRegistered" class="mt-4">
You can now log things that you've seen:
@@ -29,8 +29,7 @@
button to express your appreciation for... whatever.
</p>
<p class="mt-4">
Once someone registers you, you can log your
appreciation, too.
Once someone registers you, you can log your appreciation, too.
</p>
<p class="mt-4">

View File

@@ -8,11 +8,7 @@
>
<div class="h-full w-full object-contain" v-html="generateIcon()" />
</a>
<div
v-else
class="h-full w-full object-contain"
v-html="generateIcon()"
/>
<div v-else class="h-full w-full object-contain" v-html="generateIcon()" />
</template>
<script lang="ts">
import { toSvg } from "jdenticon";

View File

@@ -33,18 +33,18 @@ export const APP_SERVER =
export const DEFAULT_ENDORSER_API_SERVER =
import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER ||
AppString.TEST_ENDORSER_API_SERVER;
AppString.PROD_ENDORSER_API_SERVER;
export const DEFAULT_IMAGE_API_SERVER =
import.meta.env.VITE_DEFAULT_IMAGE_API_SERVER ||
AppString.TEST_IMAGE_API_SERVER;
AppString.PROD_IMAGE_API_SERVER;
export const DEFAULT_PARTNER_API_SERVER =
import.meta.env.VITE_DEFAULT_PARTNER_API_SERVER ||
AppString.TEST_PARTNER_API_SERVER;
AppString.PROD_PARTNER_API_SERVER;
export const DEFAULT_PUSH_SERVER =
import.meta.env.VITE_DEFAULT_PUSH_SERVER || "https://timesafari.app";
import.meta.env.VITE_DEFAULT_PUSH_SERVER || AppString.PROD_PUSH_SERVER;
export const IMAGE_TYPE_PROFILE = "profile";

View File

@@ -55,20 +55,7 @@ export async function updateAccountSettings(
);
const updateResult = await platform.dbExec(updateSql, updateParams);
// If no record was updated, insert a new one
if (updateResult.changes === 1) {
return true;
} else {
const columns = Object.keys(settingsChanges);
const values = Object.values(settingsChanges);
const placeholders = values.map(() => "?").join(", ");
const insertSql = `INSERT INTO settings (${columns.join(", ")}) VALUES (${placeholders})`;
const result = await platform.dbExec(insertSql, values);
return result.changes === 1;
}
return updateResult.changes === 1;
}
const DEFAULT_SETTINGS: Settings = {

View File

@@ -626,7 +626,9 @@ export const retrieveFullyDecryptedAccount = async (
return result;
};
export const retrieveAllAccountsMetadata = async (): Promise<AccountEncrypted[]> => {
export const retrieveAllAccountsMetadata = async (): Promise<
AccountEncrypted[]
> => {
const platformService = PlatformServiceFactory.getInstance();
const dbAccounts = await platformService.dbQuery(`SELECT * FROM accounts`);
const accounts = databaseUtil.mapQueryResultToValues(dbAccounts) as Account[];
@@ -643,8 +645,12 @@ export const retrieveAllAccountsMetadata = async (): Promise<AccountEncrypted[]>
// This is not accurate because they can't be decrypted, but we're removing Dexie anyway.
const identityStr = JSON.stringify(identity);
const encryptedAccount = {
identityEncrBase64: sha256(new TextEncoder().encode(identityStr)).toString(),
mnemonicEncrBase64: sha256(new TextEncoder().encode(account.mnemonic)).toString(),
identityEncrBase64: sha256(
new TextEncoder().encode(identityStr),
).toString(),
mnemonicEncrBase64: sha256(
new TextEncoder().encode(account.mnemonic),
).toString(),
...metadata,
};
return encryptedAccount as AccountEncrypted;

View File

@@ -211,7 +211,7 @@
@click="handleQRCodeClick"
>
Share Your Info
</button>
</button>
</div>
<section
@@ -1015,7 +1015,6 @@ import {
retrieveSettingsForActiveAccount,
updateAccountSettings,
} from "../db/index";
import { Account } from "../db/tables/accounts";
import { Contact } from "../db/tables/contacts";
import {
DEFAULT_PASSKEY_EXPIRATION_MINUTES,
@@ -1040,7 +1039,6 @@ import {
} from "../libs/util";
import { UserProfile } from "@/libs/partnerServer";
import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
const inputImportFileNameRef = ref<Blob>();
@@ -1174,8 +1172,6 @@ export default class AccountViewView extends Vue {
5000,
);
}
} finally {
this.loadingProfile = false;
}
}
} catch (error) {
@@ -1198,6 +1194,8 @@ export default class AccountViewView extends Vue {
},
5000,
);
} finally {
this.loadingProfile = false;
}
try {
@@ -1240,7 +1238,6 @@ export default class AccountViewView extends Vue {
*/
async initializeState() {
let settings = await databaseUtil.retrieveSettingsForActiveAccount();
console.log("settings", settings);
if (USE_DEXIE_DB) {
await db.open();
settings = await retrieveSettingsForActiveAccount();

View File

@@ -292,10 +292,7 @@
<div class="text-sm">
{{ didInfo(confirmerId) }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(confirmerId)">
<a
:href="`/did/${confirmerId}`"
class="text-blue-500"
>
<a :href="`/did/${confirmerId}`" class="text-blue-500">
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
@@ -332,10 +329,7 @@
<div class="text-sm">
{{ didInfo(confsVisibleTo) }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(confsVisibleTo)">
<a
:href="`/did/${confsVisibleTo}`"
class="text-blue-500"
>
<a :href="`/did/${confsVisibleTo}`" class="text-blue-500">
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
@@ -449,10 +443,7 @@
<span>
{{ didInfo(visDid) }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(visDid)">
<a
:href="`/did/${visDid}`"
class="text-blue-500"
>
<a :href="`/did/${visDid}`" class="text-blue-500">
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"

View File

@@ -78,7 +78,7 @@
class="block w-full rounded-l border border-r-0 border-slate-400 px-3 py-2 h-10"
/>
<button
class="px-4 rounded-r bg-green-200 border border-l-0 border-green-400"
class="px-4 rounded-r bg-green-200 border border-green-400"
@click="onClickNewContact()"
>
<font-awesome icon="plus" class="fa-fw" />
@@ -86,8 +86,8 @@
</div>
<div v-if="contacts.length > 0" class="flex justify-between">
<div class="w-full text-left">
<div v-if="!showGiveNumbers">
<div class="">
<div v-if="!showGiveNumbers" class="flex items-center">
<input
type="checkbox"
:checked="contactsSelected.length === contacts.length"
@@ -101,52 +101,29 @@
/>
<button
v-if="!showGiveNumbers"
href=""
class="text-md bg-gradient-to-b shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-3 px-3 py-1.5 rounded-md"
:style="
:class="
contactsSelected.length > 0
? 'background-image: linear-gradient(to bottom, #3b82f6, #1e40af);'
: 'background-image: linear-gradient(to bottom, #94a3b8, #374151);'
? 'text-md 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-3 px-3 py-1.5 rounded-md cursor-pointer'
: 'text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-slate-300 ml-3 px-3 py-1.5 rounded-md cursor-not-allowed'
"
data-testId="copySelectedContactsButtonTop"
@click="copySelectedContacts()"
>
Copy Selections
</button>
<button @click="showCopySelectionsInfo()">
<font-awesome
icon="circle-info"
class="text-xl text-blue-500 ml-4"
/>
Copy
</button>
<font-awesome
@click="showCopySelectionsInfo()"
icon="circle-info"
class="text-2xl text-blue-500 ml-2"
/>
</div>
</div>
<div class="w-full text-right">
<div class="flex items-center gap-2">
<button
v-if="showGiveNumbers"
href=""
class="text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md"
@click="toggleShowContactAmounts()"
>
{{
showGiveNumbers ? "Hide Hours, Offer, etc" : "See Hours, Offer, etc"
}}
</button>
</div>
</div>
<div v-if="showGiveNumbers" class="flex justify-between mt-1">
<div class="w-full text-right">
In the following, only the most recent hours are included. To see more,
click
<span
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 px-1 py-1 rounded-md"
>
<font-awesome icon="file-lines" class="fa-fw" />
</span>
<br />
<button
href=""
class="text-md bg-gradient-to-b shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-1 rounded-md mt-1"
class="text-md bg-gradient-to-b shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md"
:class="showGiveAmountsClassNames()"
@click="toggleShowGiveTotals()"
>
@@ -159,6 +136,28 @@
}}
<font-awesome icon="left-right" class="fa-fw" />
</button>
<button
href=""
class="text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md"
@click="toggleShowContactAmounts()"
>
{{
showGiveNumbers ? "Hide Actions" : "See Actions"
}}
</button>
</div>
</div>
<div v-if="showGiveNumbers" class="my-3">
<div class="w-full text-center text-sm italic text-slate-600">
Only the most recent hours are included. <br />To see more,
click
<span
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 px-1 py-0.5 rounded"
>
<font-awesome icon="file-lines" class="text-xs fa-fw" />
</span>
<br />
</div>
</div>
@@ -166,7 +165,7 @@
<ul
v-if="contacts.length > 0"
id="listContacts"
class="border-t border-slate-300 mt-1"
class="border-t border-slate-300 my-2"
>
<li
v-for="contact in filteredContacts()"
@@ -174,125 +173,127 @@
class="border-b border-slate-300 pt-1 pb-1"
data-testId="contactListItem"
>
<div class="grow overflow-hidden">
<div class="flex items-center justify-between gap-3">
<div class="flex items-center gap-3">
<input
v-if="!showGiveNumbers"
type="checkbox"
:checked="contactsSelected.includes(contact.did)"
class="ml-2 h-6 w-6 flex-shrink-0"
data-testId="contactCheckOne"
@click="
contactsSelected.includes(contact.did)
? contactsSelected.splice(
contactsSelected.indexOf(contact.did),
1,
)
: contactsSelected.push(contact.did)
"
/>
<div class="flex items-center justify-between gap-3">
<div class="flex overflow-hidden min-w-0 items-center gap-3">
<input
v-if="!showGiveNumbers"
type="checkbox"
:checked="contactsSelected.includes(contact.did)"
class="ml-2 h-6 w-6 flex-shrink-0"
data-testId="contactCheckOne"
@click="
contactsSelected.includes(contact.did)
? contactsSelected.splice(
contactsSelected.indexOf(contact.did),
1,
)
: contactsSelected.push(contact.did)
"
/>
<div
class="flex-shrink-0 w-12 h-12 flex items-center justify-center"
>
<EntityIcon
:contact="contact"
:icon-size="48"
class="inline-block align-text-bottom border border-slate-300 rounded cursor-pointer overflow-hidden"
@click="showLargeIdenticon = contact"
/>
</div>
<EntityIcon
:contact="contact"
:icon-size="48"
class="shrink-0 align-text-bottom border border-slate-300 rounded cursor-pointer overflow-hidden"
@click="showLargeIdenticon = contact"
/>
<h2 class="text-base font-semibold w-1/3 truncate flex-shrink-0">
{{ contactNameNonBreakingSpace(contact.name) }}
<div class="overflow-hidden">
<h2 class="text-base font-semibold truncate">
<router-link
:to="{
path: '/did/' + encodeURIComponent(contact.did),
}"
title="See more about this person"
>
{{ contactNameNonBreakingSpace(contact.name) }}
</router-link>
</h2>
<span>
<div class="flex gap-2 items-center">
<router-link
:to="{
path: '/did/' + encodeURIComponent(contact.did),
}"
title="See more about this person"
>
<font-awesome
icon="circle-info"
class="text-xl text-blue-500"
/>
</router-link>
<div class="flex gap-1.5 items-center overflow-hidden">
<router-link
:to="{
path: '/did/' + encodeURIComponent(contact.did),
}"
title="See more about this person"
>
<font-awesome
icon="circle-info"
class="text-base text-blue-500"
/>
</router-link>
<span class="text-sm overflow-hidden">{{
libsUtil.shortDid(contact.did)
}}</span>
</div>
<div class="text-sm">
{{ contact.notes }}
</div>
</span>
<span class="text-xs truncate">{{
contact.did
}}</span>
</div>
<div class="text-sm">
{{ contact.notes }}
</div>
</div>
</div>
<div
v-if="showGiveNumbers && contact.did != activeDid"
class="flex gap-1.5 items-end"
>
<div class='text-center'>
<div class="text-xs leading-none mb-1">From/To</div>
<div class="flex items-center">
<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.5 py-1.5 rounded-l-md"
:title="givenToMeDescriptions[contact.did] || ''"
@click="confirmShowGiftedDialog(contact.did, activeDid)"
>
{{
/* eslint-disable prettier/prettier */
showGiveTotals
? ((givenToMeConfirmed[contact.did] || 0)
+ (givenToMeUnconfirmed[contact.did] || 0))
: 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 px-2.5 py-1.5 rounded-r-md border-l"
:title="givenByMeDescriptions[contact.did] || ''"
@click="confirmShowGiftedDialog(activeDid, contact.did)"
>
{{
/* eslint-disable prettier/prettier */
showGiveTotals
? ((givenByMeConfirmed[contact.did] || 0)
+ (givenByMeUnconfirmed[contact.did] || 0))
: showGiveConfirmed
? (givenByMeConfirmed[contact.did] || 0)
: (givenByMeUnconfirmed[contact.did] || 0)
/* eslint-enable prettier/prettier */
}}
</button>
</div>
</div>
<div
v-if="showGiveNumbers && contact.did != activeDid"
class="flex gap-2 items-center"
<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-md"
data-testId="offerButton"
@click="openOfferDialog(contact.did, contact.name)"
>
<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"
:title="givenToMeDescriptions[contact.did] || ''"
@click="confirmShowGiftedDialog(contact.did, activeDid)"
>
From:
<br />
{{
/* eslint-disable prettier/prettier */
showGiveTotals
? ((givenToMeConfirmed[contact.did] || 0)
+ (givenToMeUnconfirmed[contact.did] || 0))
: showGiveConfirmed
? (givenToMeConfirmed[contact.did] || 0)
: (givenToMeUnconfirmed[contact.did] || 0)
/* eslint-enable prettier/prettier */
}}
</button>
Offer
</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"
:title="givenByMeDescriptions[contact.did] || ''"
@click="confirmShowGiftedDialog(activeDid, contact.did)"
>
To:
<br />
{{
/* eslint-disable prettier/prettier */
showGiveTotals
? ((givenByMeConfirmed[contact.did] || 0)
+ (givenByMeUnconfirmed[contact.did] || 0))
: showGiveConfirmed
? (givenByMeConfirmed[contact.did] || 0)
: (givenByMeUnconfirmed[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 px-2 py-1.5 rounded-md border border-blue-400"
data-testId="offerButton"
@click="openOfferDialog(contact.did, contact.name)"
>
Offer
</button>
<router-link
:to="{
name: 'contact-amounts',
query: { contactDid: contact.did },
}"
class="text-sm bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-1.5 rounded-md border border-slate-400"
title="See more given activity"
>
<font-awesome icon="file-lines" class="fa-fw" />
</router-link>
</div>
<router-link
:to="{
name: 'contact-amounts',
query: { contactDid: contact.did },
}"
class="text-sm bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-1.5 rounded-md"
title="See more given activity"
>
<font-awesome icon="file-lines" class="fa-fw" />
</router-link>
</div>
</div>
</li>
@@ -314,16 +315,14 @@
/>
<button
v-if="!showGiveNumbers"
href=""
class="text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-3 px-3 py-1.5 rounded-md"
:style="
:class="
contactsSelected.length > 0
? 'background-image: linear-gradient(to bottom, #3b82f6, #1e40af);'
: 'background-image: linear-gradient(to bottom, #94a3b8, #374151);'
? 'text-md 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-3 px-3 py-1.5 rounded-md cursor-pointer'
: 'text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-slate-300 ml-3 px-3 py-1.5 rounded-md cursor-not-allowed'
"
@click="copySelectedContacts()"
>
Copy Selections
Copy
</button>
</div>

View File

@@ -825,10 +825,7 @@ export default class GiftedDetails extends Vue {
);
}
if (
result.type === "error" ||
this.isGiveCreationError(result.response)
) {
if (!result.success) {
const errorMessage = this.getGiveCreationErrorMessage(result);
logger.error("Error with give creation result:", result);
this.$notify(
@@ -902,15 +899,6 @@ export default class GiftedDetails extends Vue {
// Helper functions for readability
/**
* @param result response "data" from the server
* @returns true if the result indicates an error
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
isGiveCreationError(result: any) {
return result.status !== 201 || result.data?.error;
}
/**
* @param result direct response eg. ErrorResult or SuccessResult (potentially with embedded "data")
* @returns best guess at an error message

View File

@@ -81,7 +81,11 @@ import {
} from "../libs/crypto";
import { accountsDBPromise, db } from "../db/index";
import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
import { retrieveAllAccountsMetadata, retrieveFullyDecryptedAccount, saveNewIdentity } from "../libs/util";
import {
retrieveAllAccountsMetadata,
retrieveFullyDecryptedAccount,
saveNewIdentity,
} from "../libs/util";
import { logger } from "../utils/logger";
import { Account, AccountEncrypted } from "../db/tables/accounts";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
@@ -100,13 +104,20 @@ export default class ImportAccountView extends Vue {
async mounted() {
const accounts: AccountEncrypted[] = await retrieveAllAccountsMetadata();
const decryptedAccounts: (Account | undefined)[] = await Promise.all(accounts.map(async (account) => {
return retrieveFullyDecryptedAccount(account.did);
}));
const filteredDecryptedAccounts: Account[] = decryptedAccounts.filter((account) => account !== undefined);
const decryptedAccounts: (Account | undefined)[] = await Promise.all(
accounts.map(async (account) => {
return retrieveFullyDecryptedAccount(account.did);
}),
);
const filteredDecryptedAccounts: Account[] = decryptedAccounts.filter(
(account) => account !== undefined,
);
// group by account.mnemonic
const groupedAccounts: Record<string, Account[]> = R.groupBy((a) => a.mnemonic || "", filteredDecryptedAccounts) as Record<string, Account[]>;
const groupedAccounts: Record<string, Account[]> = R.groupBy(
(a) => a.mnemonic || "",
filteredDecryptedAccounts,
) as Record<string, Account[]>;
this.didArrays = groupedAccounts;
if (Object.keys(this.didArrays).length > 0) {
@@ -125,10 +136,13 @@ export default class ImportAccountView extends Vue {
public async incrementDerivation() {
// find the maximum derivation path for the selected DIDs
const selectedArray: Array<Account> =
Object.values(this.didArrays).find((dids) => dids[0].did === this.selectedArrayFirstDid) ||
[];
Object.values(this.didArrays).find(
(dids) => dids[0].did === this.selectedArrayFirstDid,
) || [];
// extract the derivationPath array and sort it
const derivationPaths = selectedArray.map((account) => account.derivationPath);
const derivationPaths = selectedArray.map(
(account) => account.derivationPath,
);
derivationPaths.sort((a, b) => {
const aParts = a?.split("/");
const aLast = aParts?.[aParts.length - 1];
@@ -137,7 +151,9 @@ export default class ImportAccountView extends Vue {
return parseInt(aLast || "0") - parseInt(bLast || "0");
});
// we're sure there's at least one
const maxDerivPath: string = derivationPaths[derivationPaths.length - 1] as string;
const maxDerivPath: string = derivationPaths[
derivationPaths.length - 1
] as string;
const newDerivPath = nextDerivationPath(maxDerivPath);

View File

@@ -54,10 +54,7 @@
></font-awesome>
{{ issuerInfoObject?.displayName }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(issuer)">
<a
:href="`/did/${issuer}`"
class="text-blue-500"
>
<a :href="`/did/${issuer}`" class="text-blue-500">
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"

View File

@@ -230,7 +230,9 @@ export default class QuickActionBvcBeginView extends Vue {
suppressMilliseconds: true,
}) || "";
this.allMyDids = (await retrieveAllAccountsMetadata()).map((account) => account.did);
this.allMyDids = (await retrieveAllAccountsMetadata()).map(
(account) => account.did,
);
if (USE_DEXIE_DB) {
const accountsDB = await accountsDBPromise;
await accountsDB.open();