diff --git a/dfx.json b/dfx.json
index 544c923aee..4b84dc00c0 100644
--- a/dfx.json
+++ b/dfx.json
@@ -143,10 +143,7 @@
},
"website": {
"build": "",
- "source": [
- "frontend/app/build",
- "frontend/app/public"
- ],
+ "source": ["frontend/app/build", "frontend/app/public"],
"type": "assets"
},
"sns_governance": {
@@ -195,23 +192,17 @@
}
},
"ic": {
- "providers": [
- "https://ic0.app/"
- ],
+ "providers": ["https://ic0.app/"],
"type": "persistent"
},
"ic_test": {
- "providers": [
- "https://ic0.app/"
- ],
+ "providers": ["https://ic0.app/"],
"type": "persistent"
},
"web_test": {
- "providers": [
- "https://ic0.app/"
- ],
+ "providers": ["https://ic0.app/"],
"type": "persistent"
}
},
"version": 1
-}
\ No newline at end of file
+}
diff --git a/frontend/app/public/assets/human.svg b/frontend/app/public/assets/human.svg
new file mode 100644
index 0000000000..9da055ade4
--- /dev/null
+++ b/frontend/app/public/assets/human.svg
@@ -0,0 +1,34 @@
+
+
\ No newline at end of file
diff --git a/frontend/app/src/components/ErrorMessage.svelte b/frontend/app/src/components/ErrorMessage.svelte
index 95d0390894..34f90110c0 100644
--- a/frontend/app/src/components/ErrorMessage.svelte
+++ b/frontend/app/src/components/ErrorMessage.svelte
@@ -9,7 +9,8 @@
diff --git a/frontend/app/src/components/Input.svelte b/frontend/app/src/components/Input.svelte
index 267b5b244f..7449915ba2 100644
--- a/frontend/app/src/components/Input.svelte
+++ b/frontend/app/src/components/Input.svelte
@@ -135,7 +135,7 @@
}
&.invalid {
- border: 1px solid var(--error);
+ border: var(--bw) solid var(--error);
}
&::placeholder {
diff --git a/frontend/app/src/components/MatchingUser.svelte b/frontend/app/src/components/MatchingUser.svelte
index 4468652440..1e99d9290f 100644
--- a/frontend/app/src/components/MatchingUser.svelte
+++ b/frontend/app/src/components/MatchingUser.svelte
@@ -42,7 +42,10 @@
{searchTerm}
me={user.userId === $createdUser.userId}
username={user.displayName ?? user.username} />
-
+
diff --git a/frontend/app/src/components/ModalContent.svelte b/frontend/app/src/components/ModalContent.svelte
index c64d86b1db..d7c0f329ed 100644
--- a/frontend/app/src/components/ModalContent.svelte
+++ b/frontend/app/src/components/ModalContent.svelte
@@ -98,7 +98,7 @@
{#if !hideHeader}
diff --git a/frontend/app/src/components/home/CurrentChatHeader.svelte b/frontend/app/src/components/home/CurrentChatHeader.svelte
index e79378a142..0e1866739b 100644
--- a/frontend/app/src/components/home/CurrentChatHeader.svelte
+++ b/frontend/app/src/components/home/CurrentChatHeader.svelte
@@ -88,6 +88,7 @@
),
username: "@" + them.username,
eventsTTL: undefined,
+ uniquePerson: them.isUniquePerson,
};
default:
return {
@@ -104,6 +105,7 @@
typing,
),
eventsTTL: chatSummary.eventsTTL,
+ uniquePerson: false,
};
}
}
@@ -180,7 +182,10 @@
{chat.name}
-
+
{chat.username}
{:else}
{chat.name}
diff --git a/frontend/app/src/components/home/CurrentChatMessages.svelte b/frontend/app/src/components/home/CurrentChatMessages.svelte
index 7d2c784bb5..6fca245801 100644
--- a/frontend/app/src/components/home/CurrentChatMessages.svelte
+++ b/frontend/app/src/components/home/CurrentChatMessages.svelte
@@ -142,12 +142,12 @@
$: privateCommunityPreview =
$selectedCommunity !== undefined &&
$selectedCommunity.membership.role === "none" &&
- (!$selectedCommunity.public || $selectedCommunity.gate !== undefined);
+ (!$selectedCommunity.public || $selectedCommunity.gate.kind !== "no_gate");
$: privateChatPreview =
(chat.kind === "group_chat" || chat.kind === "channel") &&
chat.membership.role === "none" &&
- (!chat.public || chat.gate !== undefined);
+ (!chat.public || chat.gate.kind !== "no_gate");
$: privatePreview = privateCommunityPreview || privateChatPreview;
$: isEmptyChat = chat.latestEventIndex <= 0 || privatePreview;
@@ -309,60 +309,62 @@
{/if}
{/if}
- {#each timeline as timelineItem}
- {#if timelineItem.kind === "timeline_date"}
-
- {:else}
- {#each timelineItem.group as innerGroup (userGroupKey(innerGroup))}
- {#each innerGroup as evt, i (eventKey(evt))}
-
toggleMessageExpansion(evt, true)}
- on:collapseMessage={() => toggleMessageExpansion(evt, false)}
- on:upgrade
- on:forward
- on:retrySend
- on:startVideoCall
- event={evt} />
+ {#if !privatePreview}
+ {#each timeline as timelineItem}
+ {#if timelineItem.kind === "timeline_date"}
+
+ {:else}
+ {#each timelineItem.group as innerGroup (userGroupKey(innerGroup))}
+ {#each innerGroup as evt, i (eventKey(evt))}
+ toggleMessageExpansion(evt, true)}
+ on:collapseMessage={() => toggleMessageExpansion(evt, false)}
+ on:upgrade
+ on:forward
+ on:retrySend
+ on:startVideoCall
+ event={evt} />
+ {/each}
{/each}
- {/each}
- {/if}
- {/each}
+ {/if}
+ {/each}
+ {/if}
{#if reverseScroll}
{#if privatePreview}
diff --git a/frontend/app/src/components/home/Home.svelte b/frontend/app/src/components/home/Home.svelte
index 719c191d8a..422452d176 100644
--- a/frontend/app/src/components/home/Home.svelte
+++ b/frontend/app/src/components/home/Home.svelte
@@ -28,9 +28,10 @@
GroupChatSummary,
ChannelIdentifier,
UpdatedRules,
- CredentialGate,
- PaymentGate,
ResourceKey,
+ NervousSystemDetails,
+ AccessGateWithLevel,
+ GateCheckSucceeded,
} from "openchat-client";
import {
ChatsUpdated,
@@ -78,8 +79,7 @@
import AccountsModal from "./profile/AccountsModal.svelte";
import { querystring } from "../../routes";
import { eventListScrollTop } from "../../stores/scrollPos";
- import GateCheckFailed from "./AccessGateCheckFailed.svelte";
- import InitiateCredentialCheck from "./InitiateCredentialCheck.svelte";
+ import GateCheckFailed from "./access/AccessGateCheckFailed.svelte";
import HallOfFame from "./ChitHallOfFame.svelte";
import LeftNav from "./nav/LeftNav.svelte";
import MakeProposalModal from "./MakeProposalModal.svelte";
@@ -90,7 +90,6 @@
import LoggingInModal from "./LoggingInModal.svelte";
import AnonFooter from "./AnonFooter.svelte";
import OfflineFooter from "../OfflineFooter.svelte";
- import ApproveJoiningPaymentModal from "./ApproveJoiningPaymentModal.svelte";
import RightPanel from "./RightPanelWrapper.svelte";
import EditLabel from "../EditLabel.svelte";
import { i18nKey } from "../../i18n/i18n";
@@ -102,6 +101,7 @@
import ChallengeModal from "./ChallengeModal.svelte";
import ChitEarned from "./ChitEarned.svelte";
import { chitPopup } from "../../stores/settings";
+ import AccessGateEvaluator from "./access/AccessGateEvaluator.svelte";
type ViewProfileConfig = {
userId: string;
@@ -112,9 +112,6 @@
const client = getContext("client");
- let candidateGroup: CandidateGroupChat | undefined;
- let candidateCommunity: CommunitySummary | undefined;
- let candidateCommunityRules: Rules = defaultChatRules("community");
let convertGroup: GroupChatSummary | undefined = undefined;
let showProfileCard: ViewProfileConfig | undefined = undefined;
@@ -150,35 +147,33 @@
doubleCheck: { challenge: ResourceKey; response: ResourceKey };
};
- enum ModalType {
- None,
- SelectChat,
- Suspended,
- NoAccess,
- NewGroup,
- Wallet,
- GateCheckFailed,
- VerifyCredential,
- ApproveJoinPayment,
- HallOfFame,
- EditCommunity,
- MakeProposal,
- Registering,
- LoggingIn,
- NotFound,
- ClaimDailyChit,
- Challenge,
- }
-
- let modal = ModalType.None;
+ type ModalType =
+ | { kind: "none" }
+ | { kind: "select_chat" }
+ | { kind: "suspended" }
+ | { kind: "no_access" }
+ | { kind: "new_group"; candidate: CandidateGroupChat }
+ | { kind: "wallet" }
+ | { kind: "gate_check_failed"; gates: AccessGateWithLevel[] }
+ | { kind: "hall_of_fame" }
+ | { kind: "edit_community"; community: CommunitySummary; communityRules: Rules }
+ | { kind: "make_proposal"; chat: MultiUserChat; nervousSystem: NervousSystemDetails }
+ | { kind: "registering" }
+ | { kind: "logging_in" }
+ | { kind: "not_found" }
+ | { kind: "claim_daily_chit" }
+ | { kind: "challenge" }
+ | {
+ kind: "evaluating_access_gates";
+ group: MultiUserChat;
+ select: boolean;
+ gates: AccessGateWithLevel[];
+ level: Level;
+ };
+
+ let modal: ModalType = { kind: "none" };
let confirmActionEvent: ConfirmActionEvent | undefined;
let joining: MultiUserChat | undefined = undefined;
- let credentialCheck:
- | { group: MultiUserChat; gate: CredentialGate; select: boolean }
- | undefined = undefined;
- let joinPaymentDetails:
- | { group: MultiUserChat; gate: PaymentGate; select: boolean }
- | undefined = undefined;
let showUpgrade: boolean = false;
let share: Share = { title: "", text: "", url: "", files: [] };
let messageToForward: Message | undefined = undefined;
@@ -215,14 +210,14 @@
$: rulesAcceptanceStore = client.captureRulesAcceptanceStore;
$: {
if ($identityState.kind === "registering") {
- modal = ModalType.Registering;
+ modal = { kind: "registering" };
} else if ($identityState.kind === "logging_in") {
- modal = ModalType.LoggingIn;
- } else if ($identityState.kind === "logged_in" && modal === ModalType.Registering) {
+ modal = { kind: "logging_in" };
+ } else if ($identityState.kind === "logged_in" && modal.kind === "registering") {
console.log("We are now logged in so we are closing the register modal");
- modal = ModalType.None;
+ closeModal();
} else if ($identityState.kind === "challenging") {
- modal = ModalType.Challenge;
+ modal = { kind: "challenge" };
}
if (
$identityState.kind === "logged_in" &&
@@ -246,7 +241,7 @@
client.addEventListener("openchat_event", clientEvent);
if ($suspendedUser) {
- modal = ModalType.Suspended;
+ modal = { kind: "suspended" };
}
return () => {
@@ -294,9 +289,11 @@
function remoteVideoCallStarted(ev: RemoteVideoCallStartedEvent) {
// If current user is already in the call, or has previously been in the call, or the call started more than an hour ago, exit
- if ($activeVideoCall?.chatId === ev.detail.chatId ||
+ if (
+ $activeVideoCall?.chatId === ev.detail.chatId ||
ev.detail.currentUserIsParticipant ||
- Number(ev.detail.timestamp) < Date.now() - 60 * 60 * 1000) {
+ Number(ev.detail.timestamp) < Date.now() - 60 * 60 * 1000
+ ) {
return;
}
@@ -362,7 +359,7 @@
pageReplace(routeForChatIdentifier($chatListScope.kind, preview.location));
}
} else if (preview.kind === "failure") {
- modal = ModalType.NotFound;
+ modal = { kind: "not_found" };
return;
}
}
@@ -401,7 +398,7 @@
async function selectCommunity(id: CommunityIdentifier, clearChat = true): Promise {
const found = await client.setSelectedCommunity(id, $querystring.get("code"), clearChat);
if (!found) {
- modal = ModalType.NoAccess;
+ modal = { kind: "no_access" };
}
return found;
}
@@ -500,7 +497,7 @@
files: [],
};
pageReplace(routeForScope(client.getDefaultScope()));
- modal = ModalType.SelectChat;
+ modal = { kind: "select_chat" };
}
}
@@ -513,7 +510,7 @@
const wallet = $querystring.get("wallet");
if (wallet !== null) {
- modal = ModalType.Wallet;
+ modal = { kind: "wallet" };
pageReplace(removeQueryStringParam("wallet"));
}
@@ -524,7 +521,7 @@
const hof = $querystring.get("hof");
if (hof !== null) {
- modal = ModalType.HallOfFame;
+ modal = { kind: "hall_of_fame" };
pageReplace(removeQueryStringParam("hof"));
}
@@ -569,16 +566,12 @@
}
function leaderboard() {
- modal = ModalType.HallOfFame;
+ modal = { kind: "hall_of_fame" };
}
function closeModal() {
- modal = ModalType.None;
- candidateGroup = undefined;
- candidateCommunity = undefined;
+ modal = { kind: "none" };
joining = undefined;
- credentialCheck = undefined;
- joinPaymentDetails = undefined;
}
function closeNoAccess() {
@@ -754,7 +747,7 @@
function forwardMessage(ev: CustomEvent) {
messageToForward = ev.detail;
- modal = ModalType.SelectChat;
+ modal = { kind: "select_chat" };
}
function showGroupMembers(ev: CustomEvent) {
@@ -814,7 +807,7 @@
function showMakeProposalModal() {
if (nervousSystem !== undefined && selectedMultiUserChat !== undefined) {
- modal = ModalType.MakeProposal;
+ modal = { kind: "make_proposal", chat: selectedMultiUserChat, nervousSystem };
}
}
@@ -839,56 +832,61 @@
}
}
- function credentialReceived(ev: CustomEvent) {
- if (credentialCheck !== undefined) {
- const { group, select } = credentialCheck;
+ function accessGatesEvaluated(ev: CustomEvent) {
+ if (modal.kind === "evaluating_access_gates") {
+ const { group, select } = modal;
closeModal();
doJoinGroup(group, select, ev.detail);
}
}
- function onJoined() {
- if (joinPaymentDetails?.select) {
- page(routeForChatIdentifier($chatListScope.kind, joinPaymentDetails.group.id));
- }
- closeModal();
- }
+ /**
+ * When we try to join a group we need to first scrutinise the access gates and
+ * see whether any of them require client side action before we can proceed with the
+ * call to the back end. I there are gates which require action, we need to perform
+ * those actions one by one until they are all done and then feed their results
+ * back into this function.
+ */
async function doJoinGroup(
group: MultiUserChat,
select: boolean,
- credential: string | undefined,
+ gateCheck: GateCheckSucceeded | undefined,
): Promise {
joining = group;
- if (group.gate.kind === "credential_gate" && credential === undefined) {
- credentialCheck = { group, select, gate: group.gate };
- modal = ModalType.VerifyCredential;
- return Promise.resolve();
- } else if (group.gate.kind === "payment_gate") {
- joinPaymentDetails = { group, select, gate: group.gate };
- modal = ModalType.ApproveJoinPayment;
- return Promise.resolve();
- } else if (group.kind === "channel") {
- const community = client.getCommunityForChannel(group.id);
- if (community?.gate.kind === "credential_gate" && credential === undefined) {
- credentialCheck = { group, select, gate: community.gate };
- modal = ModalType.VerifyCredential;
- return Promise.resolve();
- } else if (community?.gate.kind === "payment_gate") {
- joinPaymentDetails = { group, select, gate: community.gate };
- modal = ModalType.ApproveJoinPayment;
- return Promise.resolve();
+ const credentials = gateCheck?.credentials ?? [];
+
+ if (gateCheck === undefined) {
+ const gates = client.accessGatesForChat(group);
+ const passed = client.doesUserMeetAccessGates(gates);
+
+ if (!passed) {
+ /**
+ * If we cannot already tell that the user passes the access gate(s), check if there are any gates that require front end
+ * pre-processing.
+ */
+ if (client.gatePreprocessingRequired(gates)) {
+ modal = {
+ kind: "evaluating_access_gates",
+ group,
+ select,
+ gates,
+ level: group.level,
+ };
+ return Promise.resolve();
+ }
}
}
return client
- .joinGroup(group, credential)
+ .joinGroup(group, credentials)
.then((resp) => {
if (resp.kind === "blocked") {
toastStore.showFailureToast(i18nKey("youreBlocked"));
joining = undefined;
} else if (resp.kind === "gate_check_failed") {
- modal = ModalType.GateCheckFailed;
+ const gates = client.accessGatesForChat(group);
+ modal = { kind: "gate_check_failed", gates };
} else if (resp.kind !== "success") {
toastStore.showFailureToast(
i18nKey("joinGroupFailed", undefined, group.level, true),
@@ -969,7 +967,7 @@
}
function showWallet() {
- modal = ModalType.Wallet;
+ modal = { kind: "wallet" };
}
function newChannel() {
@@ -985,66 +983,70 @@
? { kind: "channel", communityId: $chatListScope.id.communityId, channelId: "" }
: { kind: "group_chat", groupId: "" };
- modal = ModalType.NewGroup;
- candidateGroup = {
- id,
- kind: "candidate_group_chat",
- name: "",
- description: "",
- historyVisible: true,
- public: false,
- frozen: false,
- members: [],
- permissions: {
- changeRoles: "admin",
- removeMembers: "moderator",
- deleteMessages: "moderator",
- updateGroup: "admin",
- pinMessages: "admin",
- inviteUsers: "admin",
- mentionAllMembers: "member",
- reactToMessages: "member",
- startVideoCall: "member",
- messagePermissions: {
- default: "member",
- p2pSwap: "none",
+ modal = {
+ kind: "new_group",
+ candidate: {
+ id,
+ kind: "candidate_group_chat",
+ name: "",
+ description: "",
+ historyVisible: true,
+ public: false,
+ frozen: false,
+ members: [],
+ permissions: {
+ changeRoles: "admin",
+ removeMembers: "moderator",
+ deleteMessages: "moderator",
+ updateGroup: "admin",
+ pinMessages: "admin",
+ inviteUsers: "admin",
+ mentionAllMembers: "member",
+ reactToMessages: "member",
+ startVideoCall: "member",
+ messagePermissions: {
+ default: "member",
+ p2pSwap: "none",
+ },
+ threadPermissions: undefined,
+ },
+ rules: { ...defaultChatRules(level), newVersion: false },
+ gate: { kind: "no_gate" },
+ level,
+ membership: {
+ ...nullMembership(),
+ role: "owner",
},
- threadPermissions: undefined,
- },
- rules: { ...defaultChatRules(level), newVersion: false },
- gate: { kind: "no_gate" },
- level,
- membership: {
- ...nullMembership(),
- role: "owner",
},
};
}
function editGroup(ev: CustomEvent<{ chat: MultiUserChat; rules: UpdatedRules | undefined }>) {
- modal = ModalType.NewGroup;
const chat = ev.detail.chat;
let level: Level = chat.id.kind === "group_chat" ? "group" : "channel";
let rules = ev.detail.rules ?? { ...defaultChatRules(level), newVersion: false };
- candidateGroup = {
- id: chat.id,
- kind: "candidate_group_chat",
- name: chat.name,
- description: chat.description,
- historyVisible: chat.historyVisible,
- public: chat.public,
- frozen: chat.frozen,
- members: [],
- permissions: { ...chat.permissions },
- rules,
- avatar: {
- blobUrl: chat.blobUrl,
- blobData: chat.blobData,
+ modal = {
+ kind: "new_group",
+ candidate: {
+ id: chat.id,
+ kind: "candidate_group_chat",
+ name: chat.name,
+ description: chat.description,
+ historyVisible: chat.historyVisible,
+ public: chat.public,
+ frozen: chat.frozen,
+ members: [],
+ permissions: { ...chat.permissions },
+ rules,
+ avatar: {
+ blobUrl: chat.blobUrl,
+ blobData: chat.blobData,
+ },
+ gate: chat.gate,
+ level,
+ membership: chat.membership,
+ eventsTTL: chat.eventsTTL,
},
- gate: chat.gate,
- level,
- membership: chat.membership,
- eventsTTL: chat.eventsTTL,
};
}
@@ -1063,7 +1065,7 @@
async function createDirectChat(chatId: DirectChatIdentifier): Promise {
if (!(await client.createDirectChat(chatId))) {
- modal = ModalType.NotFound;
+ modal = { kind: "not_found" };
return false;
}
@@ -1075,15 +1077,19 @@
const maxIndex = $communities
.values()
.reduce((m, c) => (c.membership.index > m ? c.membership.index : m), 0);
- candidateCommunity = createCandidateCommunity("", maxIndex + 1);
- candidateCommunityRules = defaultChatRules("community");
- modal = ModalType.EditCommunity;
+ modal = {
+ kind: "edit_community",
+ community: createCandidateCommunity("", maxIndex + 1),
+ communityRules: defaultChatRules("community"),
+ };
}
function editCommunity(ev: CustomEvent) {
- candidateCommunity = ev.detail;
- candidateCommunityRules = $currentCommunityRules ?? defaultChatRules("community");
- modal = ModalType.EditCommunity;
+ modal = {
+ kind: "edit_community",
+ community: ev.detail,
+ communityRules: $currentCommunityRules ?? defaultChatRules("community"),
+ };
}
function convertGroupToCommunity(ev: CustomEvent) {
@@ -1141,7 +1147,7 @@
(modal = ModalType.HallOfFame)}
+ on:halloffame={() => (modal = { kind: "hall_of_fame" })}
on:newGroup={() => newGroup("group")}
on:communityDetails={communityDetails}
on:newChannel={newChannel}
@@ -1149,14 +1155,14 @@
on:deleteCommunity={triggerConfirm}
on:upgrade={upgrade}
on:claimDailyChit={() => {
- modal = ModalType.ClaimDailyChit;
+ modal = { kind: "claim_daily_chit" };
}} />
{/if}
{#if $layoutStore.showLeft}
(modal = ModalType.HallOfFame)}
+ on:halloffame={() => (modal = { kind: "hall_of_fame" })}
on:newGroup={() => newGroup("group")}
on:profile={showProfile}
on:communityDetails={communityDetails}
@@ -1235,62 +1241,59 @@
(showUpgrade = false)} />
{/if}
-{#if modal === ModalType.Registering}
+{#if modal.kind === "registering"}
client.logout()}
on:createdUser={(ev) => client.onCreatedUser(ev.detail)} />
-{:else if modal !== ModalType.None}
+{:else if modal.kind !== "none"}
- {#if modal === ModalType.SelectChat}
+ {#if modal.kind === "select_chat"}
- {:else if modal === ModalType.Suspended}
+ {:else if modal.kind === "suspended"}
- {:else if modal === ModalType.NoAccess}
+ {:else if modal.kind === "no_access"}
- {:else if modal === ModalType.NotFound}
+ {:else if modal.kind === "not_found"}
- {:else if modal === ModalType.GateCheckFailed && joining !== undefined}
-
- {:else if modal === ModalType.VerifyCredential && credentialCheck !== undefined}
-
+ {:else if modal.kind === "evaluating_access_gates"}
+
- {:else if modal === ModalType.ApproveJoinPayment && joinPaymentDetails !== undefined}
-
- {:else if modal === ModalType.NewGroup && candidateGroup !== undefined}
-
- {:else if modal === ModalType.EditCommunity && candidateCommunity !== undefined}
+ on:success={accessGatesEvaluated} />
+ {:else if modal.kind === "new_group"}
+
+ {:else if modal.kind === "edit_community"}
- {:else if modal === ModalType.Wallet}
+ {:else if modal.kind === "wallet"}
- {:else if modal === ModalType.HallOfFame}
+ {:else if modal.kind === "hall_of_fame"}
(modal = ModalType.ClaimDailyChit)}
+ on:streak={() => (modal = { kind: "claim_daily_chit" })}
+ on:close={closeModal} />
+ {:else if modal.kind === "make_proposal"}
+
- {:else if modal === ModalType.MakeProposal && selectedMultiUserChat !== undefined && nervousSystem !== undefined}
-
- {:else if modal === ModalType.LoggingIn}
+ {:else if modal.kind === "logging_in"}
- {:else if modal === ModalType.ClaimDailyChit}
+ {:else if modal.kind === "claim_daily_chit"}
- {:else if modal === ModalType.Challenge}
+ {:else if modal.kind === "challenge"}
{/if}
diff --git a/frontend/app/src/components/home/InitiateCredentialCheck.svelte b/frontend/app/src/components/home/InitiateCredentialCheck.svelte
deleted file mode 100644
index ad165174e3..0000000000
--- a/frontend/app/src/components/home/InitiateCredentialCheck.svelte
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
-
-
- {#if failed}
-
-
-
- {:else}
-
- {/if}
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/app/src/components/home/LoggingInModal.svelte b/frontend/app/src/components/home/LoggingInModal.svelte
index ed0d9640d3..44e27a69d6 100644
--- a/frontend/app/src/components/home/LoggingInModal.svelte
+++ b/frontend/app/src/components/home/LoggingInModal.svelte
@@ -3,15 +3,11 @@
import { createEventDispatcher, getContext, onMount } from "svelte";
import { _ } from "svelte-i18n";
import { AuthProvider, Poller, type OpenChat } from "openchat-client";
- import InternetIdentityLogo from "../landingpages/InternetIdentityLogo.svelte";
import { i18nKey } from "../../i18n/i18n";
import Translatable from "../Translatable.svelte";
- import EmailIcon from "svelte-material-icons/EmailOutline.svelte";
- import SendIcon from "svelte-material-icons/Send.svelte";
import CopyIcon from "svelte-material-icons/ContentCopy.svelte";
import FancyLoader from "../icons/FancyLoader.svelte";
import Button from "../Button.svelte";
- import Input from "../Input.svelte";
import { configKeys } from "../../utils/config";
import { ECDSAKeyIdentity } from "@dfinity/identity";
import ButtonGroup from "../ButtonGroup.svelte";
@@ -19,6 +15,7 @@
import { iconSize } from "../../stores/iconSize";
import { toastStore } from "../../stores/toast";
import { querystring } from "../../routes";
+ import ChooseSignInOption from "./profile/ChooseSignInOption.svelte";
const client = getContext("client");
const dispatch = createEventDispatcher();
@@ -26,17 +23,15 @@
let state: "options" | "logging-in" = "options";
let mode: "signin" | "signup" = "signin";
let email = "";
- let showMore = false;
let emailSignInPoller: Poller | undefined = undefined;
let error: string | undefined = undefined;
let verificationCode: string | undefined = undefined;
+ let emailInvalid = false;
+ $: restrictTo = new Set($querystring.getAll("auth"));
$: anonUser = client.anonUser;
$: identityState = client.identityState;
$: selectedAuthProviderStore = client.selectedAuthProviderStore;
- $: options = buildOptions($selectedAuthProviderStore, mode);
- $: emailInvalid = !isEmailValid(email);
- $: showAllOptions = $selectedAuthProviderStore === undefined || showMore || mode === "signup";
$: loggingInWithEmail =
state === "logging-in" && $selectedAuthProviderStore === AuthProvider.EMAIL;
$: loggingInWithEth = state === "logging-in" && $selectedAuthProviderStore === AuthProvider.ETH;
@@ -74,58 +69,8 @@
dispatch("close");
}
- function buildOptions(
- selected: AuthProvider | undefined,
- mode: "signin" | "signup",
- ): AuthProvider[] {
- let options = [];
- const supportsII = "PublicKeyCredential" in window;
-
- options.push(AuthProvider.EMAIL);
-
- if (supportsII) {
- options.push(AuthProvider.II);
- options.push(AuthProvider.ETH);
- options.push(AuthProvider.SOL);
-
- if (mode === "signin") {
- options.push(AuthProvider.NFID);
- }
- }
-
- const restrictTo = new Set($querystring.getAll("auth"));
- if (restrictTo.size > 0) {
- options = options.filter((o) => {
- return (
- (o === AuthProvider.II && restrictTo.has("II")) ||
- (o === AuthProvider.EMAIL && restrictTo.has("EMAIL")) ||
- (o === AuthProvider.ETH && restrictTo.has("ETH")) ||
- (o === AuthProvider.SOL && restrictTo.has("SOL")) ||
- (o === AuthProvider.NFID && restrictTo.has("NFID"))
- );
- });
- }
-
- if (selected !== undefined) {
- let i = options.findIndex((p) => p === selected);
-
- if (i >= 0) {
- if (selected === AuthProvider.EMAIL) {
- email = localStorage.getItem(configKeys.selectedAuthEmail) ?? "";
- }
-
- options.splice(i, 1);
- options.splice(0, 0, selected);
- }
- }
- return options;
- }
-
- function isEmailValid(email: string): boolean {
- return email.length > 0;
- }
-
- function login(provider: AuthProvider) {
+ function login(ev: CustomEvent) {
+ const provider = ev.detail;
if (emailInvalid && provider === AuthProvider.EMAIL) {
return;
}
@@ -216,10 +161,6 @@
state = "options";
}
- function providerName(provider: AuthProvider): string {
- return provider === AuthProvider.NFID ? "NFID (Legacy)" : provider;
- }
-
function toggleMode() {
if (mode === "signin") {
client.gaTrack("signup_link_clicked", "registration");
@@ -262,98 +203,21 @@
{#if state === "options"}
-
- {#each options as provider, i}
- {#if showAllOptions || i === 0}
-
1 && i === 0 ? "separate" : ""
- }`}>
- {#if provider === AuthProvider.EMAIL}
-
-
-
-
-
- login(provider)}
- placeholder={i18nKey(
- mode === "signin"
- ? "loginDialog.signinEmailPlaceholder"
- : "loginDialog.signupEmailPlaceholder",
- )} />
-
-
-
- {:else}
-
-
- {#if provider === AuthProvider.II}
-
- {:else if provider === AuthProvider.ETH}
-
- {:else if provider === AuthProvider.SOL}
-
- {:else if provider === AuthProvider.NFID}
-
- {/if}
-
-
-
- {/if}
-
- {/if}
- {/each}
-
- {#if !showAllOptions && options.length > 1}
-
- {/if}
+
-
{:else if loggingInWithEmail}
@@ -412,32 +276,6 @@
padding: 0;
}
- :global(.login .email .input-wrapper) {
- margin-bottom: 0;
- }
-
- :global(.login .auth-option button) {
- border-radius: 0 $sp2 $sp2 0;
- }
-
- :global(.login .email button) {
- height: $height;
- width: 50px;
- padding: 0 $sp3 !important;
- border-radius: 0 $sp2 $sp2 0;
- }
-
- :global(.login .email .input-wrapper input) {
- border-radius: 0;
- box-shadow: none;
- border-right: 1px solid var(--bd);
- height: $height;
- }
-
- :global(.login .email [data-lastpass-icon-root]) {
- display: none;
- }
-
:global(.login .error) {
margin-bottom: 0;
}
@@ -485,80 +323,12 @@
gap: $sp4;
}
- .center {
- display: flex;
- justify-content: center;
- align-items: center;
- }
-
a:hover {
text-decoration: underline;
}
- .options {
- display: flex;
- gap: 12px;
- flex-direction: column;
- align-items: center;
- margin-bottom: $sp3;
- width: 100%;
-
- .option {
- width: 100%;
- max-width: 440px;
- display: flex;
- align-items: center;
- gap: 12px;
-
- .email {
- flex: 1;
- display: flex;
- justify-content: space-between;
- align-items: center;
-
- .email-txt {
- flex: auto;
- }
- }
-
- .auth-option {
- display: flex;
- justify-content: space-between;
- align-items: center;
- flex: auto;
- }
-
- &.separate {
- margin-bottom: $sp2;
- border-bottom: 1px solid var(--bd);
- padding-bottom: $sp4;
- }
- }
-
- .icon {
- flex: 0 0 60px;
- width: 60px;
- height: $height;
- border-radius: $sp2 0 0 $sp2;
- border-right: 1px solid var(--bd);
- display: flex;
- align-items: center;
- justify-content: center;
- background-color: var(--input-bg);
-
- .nfid-img {
- width: 40px;
- }
-
- .eth-img,
- .sol-img {
- width: 30px;
- }
- }
-
- .change-mode {
- margin-top: $sp4;
- }
+ .change-mode {
+ margin-top: $sp4;
}
.code-wrapper {
diff --git a/frontend/app/src/components/home/PreviewFooter.svelte b/frontend/app/src/components/home/PreviewFooter.svelte
index 5955bb57cc..1a4ac81aeb 100644
--- a/frontend/app/src/components/home/PreviewFooter.svelte
+++ b/frontend/app/src/components/home/PreviewFooter.svelte
@@ -1,13 +1,13 @@
+
+
+
+
+
+
+
+ {#if isLeafGate(gate)}
+
+ {:else if isCompositeGate(gate)}
+ {#each gate.gates as subgate, i (`${subgate.kind} + ${i}`)}
+
(selectedGateIndex = i)}>
+
+
+
+ {/each}
+ {/if}
+ {#if editable}
+
+ {/if}
+
+
+
+
+
+
+
+
+
diff --git a/frontend/app/src/components/home/access/AccessGateCheckFailed.svelte b/frontend/app/src/components/home/access/AccessGateCheckFailed.svelte
new file mode 100644
index 0000000000..cdd842c11c
--- /dev/null
+++ b/frontend/app/src/components/home/access/AccessGateCheckFailed.svelte
@@ -0,0 +1,27 @@
+
+
+
+
+
+ {#each gates as gate}
+
+ {/each}
+
+
+
+
diff --git a/frontend/app/src/components/home/access/AccessGateControl.svelte b/frontend/app/src/components/home/access/AccessGateControl.svelte
new file mode 100644
index 0000000000..deed56ddfc
--- /dev/null
+++ b/frontend/app/src/components/home/access/AccessGateControl.svelte
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
{$_("access.chooseGate")}
+
+
+
+
+
diff --git a/frontend/app/src/components/home/access/AccessGateEvaluator.svelte b/frontend/app/src/components/home/access/AccessGateEvaluator.svelte
new file mode 100644
index 0000000000..98fbfb7d15
--- /dev/null
+++ b/frontend/app/src/components/home/access/AccessGateEvaluator.svelte
@@ -0,0 +1,210 @@
+
+
+
+
+ {#if currentGate}
+ {#if isCompositeGate(currentGate) && currentGate.operator === "or"}
+
+
+
+
+
+ {#each currentGate.gates as subgate, i}
+
+
toggleIndex(i, currentGate)}
+ label={i18nKey(subgate.kind)}
+ id={`subgate_${i}`}>
+
+
+
+ {/each}
+ {:else if isCredentialGate(currentGate)}
+
+ {:else if isUniquePersonGate(currentGate)}
+
+ {:else if isPaymentGate(currentGate)}
+
+ {:else if isLifetimeDiamondGate(currentGate)}
+
+ {:else if isDiamondGate(currentGate)}
+
+ {/if}
+ {/if}
+
+
+
+
+ {#if currentGate !== undefined}
+ {#if isCompositeGate(currentGate)}
+
+ {/if}
+ {:else}
+
+ {/if}
+
+
+
+
+
diff --git a/frontend/app/src/components/home/AccessGateIcon.svelte b/frontend/app/src/components/home/access/AccessGateIcon.svelte
similarity index 71%
rename from frontend/app/src/components/home/AccessGateIcon.svelte
rename to frontend/app/src/components/home/access/AccessGateIcon.svelte
index d200ac7669..43203f62c7 100644
--- a/frontend/app/src/components/home/AccessGateIcon.svelte
+++ b/frontend/app/src/components/home/access/AccessGateIcon.svelte
@@ -1,8 +1,10 @@
-{#if gate.kind !== "no_gate"}
- {#if gate.kind === "diamond_gate"}
+{#if showDetail}
+
(showDetail = false)}
+ {gate}
+ editable={false} />
+{/if}
+
+
+
+
+ {#if gate.kind === "no_gate" && showNoGate}
- dispatch("upgrade")} slot="target" class="diamond">
+
+
+
+
+
+
+
+
+
+ {:else if gate.kind === "composite_gate"}
+
+
+
+
+
+
+
+
+
+
+ {:else if gate.kind === "diamond_gate"}
+
+
@@ -81,7 +130,7 @@
{:else if gate.kind === "lifetime_diamond_gate"}
- dispatch("upgrade")} slot="target" class="diamond">
+
@@ -92,7 +141,7 @@
{:else if gate.kind === "unique_person_gate"}
- dispatch("upgrade")} slot="target" class="diamond">
+
@@ -167,7 +216,7 @@
{/if}
-{/if}
+
diff --git a/frontend/app/src/components/home/AccessGateParameters.svelte b/frontend/app/src/components/home/access/AccessGateParameters.svelte
similarity index 96%
rename from frontend/app/src/components/home/AccessGateParameters.svelte
rename to frontend/app/src/components/home/access/AccessGateParameters.svelte
index d6492a2a59..47355475e2 100644
--- a/frontend/app/src/components/home/AccessGateParameters.svelte
+++ b/frontend/app/src/components/home/access/AccessGateParameters.svelte
@@ -7,8 +7,8 @@
type TokenBalanceGate,
} from "openchat-client";
import { getContext } from "svelte";
- import Translatable from "../Translatable.svelte";
- import { i18nKey } from "../../i18n/i18n";
+ import Translatable from "../../Translatable.svelte";
+ import { i18nKey } from "../../../i18n/i18n";
import CredentialGateSummary from "./CredentialGateSummary.svelte";
const client = getContext("client");
diff --git a/frontend/app/src/components/home/access/AccessGateSummary.svelte b/frontend/app/src/components/home/access/AccessGateSummary.svelte
new file mode 100644
index 0000000000..dee78a0fc5
--- /dev/null
+++ b/frontend/app/src/components/home/access/AccessGateSummary.svelte
@@ -0,0 +1,94 @@
+
+
+{#if showDetail}
+ (showDetail = false)}
+ bind:gate
+ {editable} />
+{/if}
+
+{#if gate.kind !== "no_gate" || showNoGate}
+
+
+
+
+
+ {#if gateText !== undefined}
+
+ {/if}
+
+
+{/if}
+
+
diff --git a/frontend/app/src/components/home/access/CredentialGateEvaluator.svelte b/frontend/app/src/components/home/access/CredentialGateEvaluator.svelte
new file mode 100644
index 0000000000..d1330f0017
--- /dev/null
+++ b/frontend/app/src/components/home/access/CredentialGateEvaluator.svelte
@@ -0,0 +1,89 @@
+
+
+
+
+ {#if failed}
+
+
+
+ {:else}
+
+ {/if}
+
+
+
+
+
+
+
+
+
diff --git a/frontend/app/src/components/home/CredentialGatePopup.svelte b/frontend/app/src/components/home/access/CredentialGatePopup.svelte
similarity index 74%
rename from frontend/app/src/components/home/CredentialGatePopup.svelte
rename to frontend/app/src/components/home/access/CredentialGatePopup.svelte
index 28ef9f8308..903bdd7a12 100644
--- a/frontend/app/src/components/home/CredentialGatePopup.svelte
+++ b/frontend/app/src/components/home/access/CredentialGatePopup.svelte
@@ -1,8 +1,8 @@
diff --git a/frontend/app/src/components/home/CredentialGateSummary.svelte b/frontend/app/src/components/home/access/CredentialGateSummary.svelte
similarity index 85%
rename from frontend/app/src/components/home/CredentialGateSummary.svelte
rename to frontend/app/src/components/home/access/CredentialGateSummary.svelte
index e42538687f..a279f5c240 100644
--- a/frontend/app/src/components/home/CredentialGateSummary.svelte
+++ b/frontend/app/src/components/home/access/CredentialGateSummary.svelte
@@ -1,7 +1,7 @@
diff --git a/frontend/app/src/components/home/CredentialSelector.svelte b/frontend/app/src/components/home/access/CredentialSelector.svelte
similarity index 81%
rename from frontend/app/src/components/home/CredentialSelector.svelte
rename to frontend/app/src/components/home/access/CredentialSelector.svelte
index 9c80666672..97af478d60 100644
--- a/frontend/app/src/components/home/CredentialSelector.svelte
+++ b/frontend/app/src/components/home/access/CredentialSelector.svelte
@@ -1,20 +1,21 @@
{#if selectedCredentialIssuer}
-
+
-
+
-
+
-
+
@@ -140,36 +145,42 @@
{#each credentialArguments as [name, value]}
-
+
-
+
-
deleteArgument(name)} class="delete-icon">
-
-
+ {#if editable}
+
deleteArgument(name)} class="delete-icon">
+
+
+ {/if}
{/each}
-
-
-
+ {#if editable}
+
+
+
+ {/if}
{/if}
+
+
+
+
+ {/if}
+
+
+
diff --git a/frontend/app/src/components/home/profile/ViewUserProfile.svelte b/frontend/app/src/components/home/profile/ViewUserProfile.svelte
index c82e223f19..f9ecb28a0f 100644
--- a/frontend/app/src/components/home/profile/ViewUserProfile.svelte
+++ b/frontend/app/src/components/home/profile/ViewUserProfile.svelte
@@ -288,7 +288,10 @@
@{profile.username}
-
+
{#if user !== undefined && $selectedChat !== undefined && $selectedChat.kind !== "direct_chat"}
;
type FeeData = RemoteData, string>;
@@ -40,31 +44,36 @@
index: 0,
duration: i18nKey("upgrade.oneMonth"),
fee: "oneMonth",
+ enabled: !lifetime,
},
{
index: 1,
duration: i18nKey("upgrade.threeMonths"),
fee: "threeMonths",
+ enabled: !lifetime,
},
{
index: 2,
duration: i18nKey("upgrade.oneYear"),
fee: "oneYear",
+ enabled: !lifetime,
},
{
index: 3,
duration: i18nKey("upgrade.lifetime"),
fee: "lifetime",
+ enabled: true,
},
];
let autoRenew = true;
- let selectedOption: Option | undefined = options[0];
+ let selectedOption: Option | undefined = options[lifetime ? 3 : 0];
type Option = {
index: number;
duration: ResourceKey;
fee: FeeKey;
+ enabled: boolean;
};
let diamondFees: FeeData = {
@@ -116,11 +125,14 @@
autoRenew && selectedDuration !== "lifetime",
toPayE8s,
)
- .then((success) => {
- if (success) {
+ .then((resp) => {
+ if (resp.kind === "success") {
confirmed = true;
+ dispatch("success", resp.proof);
} else {
- toastStore.showFailureToast(i18nKey("upgrade.paymentFailed"));
+ const errorKey = "upgrade.paymentFailed";
+ error = errorKey;
+ toastStore.showFailureToast(i18nKey(errorKey));
}
})
.finally(() => (confirming = false));
@@ -142,13 +154,15 @@
});
-
+
{#if confirming}
{:else if confirmed}
{:else}
-
+ {#if showExpiry}
+
+ {/if}
{#each options as option}
@@ -156,9 +170,14 @@
role="button"
tabindex="0"
class="option"
+ class:disabled={!option.enabled}
class:insufficientFunds={insufficientFunds && !refreshingBalance}
class:selected={selectedOption?.index === option.index}
- on:click={() => (selectedOption = option)}>
+ on:click={() => {
+ if (option.enabled) {
+ selectedOption = option;
+ }
+ }}>
-
-
(autoRenew = !autoRenew)}
- label={i18nKey("upgrade.autorenew")}
- align={"start"}
- disabled={selectedDuration === "lifetime"}
- checked={autoRenew && selectedDuration !== "lifetime"}>
-
-
-
-
-
-
- {#if insufficientFunds && !refreshingBalance}
-
- {/if}
+ {#if !lifetime}
+
+
(autoRenew = !autoRenew)}
+ label={i18nKey("upgrade.autorenew")}
+ align={"start"}
+ disabled={selectedDuration === "lifetime"}
+ checked={autoRenew && selectedDuration !== "lifetime"}>
+
+
+
+
+
+
+
+
+ {/if}
+
{/if}
@@ -224,12 +249,15 @@
small={!$mobileWidth}
secondary
on:click={cancel}>
-
+ {#if allowBack}
+
+ {/if}