mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-10-03 02:10:55 +00:00
Check memebership status when logging in
This commit is contained in:
parent
2464df18f3
commit
7307bafbf5
@ -3,14 +3,14 @@ import { Progress } from '@kobalte/core';
|
||||
|
||||
import styles from './Uploader.module.scss';
|
||||
import { uploadServer } from '../../uploadSocket';
|
||||
import { createStore, reconcile } from 'solid-js/store';
|
||||
import { createStore } from 'solid-js/store';
|
||||
import { NostrEOSE, NostrEvent, NostrEventContent, NostrEventType, NostrMediaUploaded } from '../../types/primal';
|
||||
import { readUploadTime, saveUploadTime } from '../../lib/localStore';
|
||||
import { startTimes, uploadMediaCancel, uploadMediaChunk, uploadMediaConfirm } from '../../lib/media';
|
||||
import { sha256, uuidv4 } from '../../utils';
|
||||
import { Kind, uploadLimit } from '../../constants';
|
||||
import ButtonGhost from '../Buttons/ButtonGhost';
|
||||
import { isAccountVerified } from '../../lib/profile';
|
||||
import { useAccountContext } from '../../contexts/AccountContext';
|
||||
|
||||
const MB = 1024 * 1024;
|
||||
const maxParallelChunks = 5;
|
||||
@ -44,6 +44,7 @@ const Uploader: Component<{
|
||||
onCancel?: () => void,
|
||||
onSuccsess?: (url: string) => void,
|
||||
}> = (props) => {
|
||||
const account = useAccountContext();
|
||||
|
||||
const [uploadState, setUploadState] = createStore<UploadState>({
|
||||
isUploading: false,
|
||||
@ -113,7 +114,7 @@ const Uploader: Component<{
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
calcUploadLimit(props.nip05);
|
||||
calcUploadLimit(account?.membershipStatus.tier);
|
||||
});
|
||||
|
||||
onCleanup(() => {
|
||||
@ -127,21 +128,14 @@ const Uploader: Component<{
|
||||
}
|
||||
});
|
||||
|
||||
const calcUploadLimit = (nip05: string | undefined) => {
|
||||
const calcUploadLimit = (membershipTier: string | undefined) => {
|
||||
|
||||
if (!nip05) {
|
||||
setUploadState('uploadLimit', () => uploadLimit.regular);
|
||||
return;
|
||||
}
|
||||
|
||||
isAccountVerified(nip05).then(profile => {
|
||||
if (profile && profile.pubkey === props.publicKey && nip05.endsWith && nip05.endsWith('primal.net')) {
|
||||
if (membershipTier === 'premium') {
|
||||
setUploadState('uploadLimit', () => uploadLimit.premium);
|
||||
return;
|
||||
}
|
||||
|
||||
setUploadState('uploadLimit', () => uploadLimit.regular);
|
||||
});
|
||||
};
|
||||
|
||||
const subTo = (socket: WebSocket, subId: string, cb: (type: NostrEventType, subId: string, content?: NostrEventContent) => void ) => {
|
||||
|
@ -17,11 +17,12 @@ import {
|
||||
NostrMutedContent,
|
||||
NostrRelays,
|
||||
NostrWindow,
|
||||
MembershipStatus,
|
||||
PrimalNote,
|
||||
PrimalUser,
|
||||
} from '../types/primal';
|
||||
import { Kind, pinEncodePrefix, relayConnectingTimeout } from "../constants";
|
||||
import { isConnected, refreshSocketListeners, removeSocketListeners, socket, subscribeTo, reset } from "../sockets";
|
||||
import { isConnected, refreshSocketListeners, removeSocketListeners, socket, subscribeTo, reset, subTo } from "../sockets";
|
||||
import { sendContacts, sendLike, sendMuteList, triggerImportEvents } from "../lib/notes";
|
||||
// @ts-ignore Bad types in nostr-tools
|
||||
import { generatePrivateKey, Relay, getPublicKey as nostrGetPubkey, nip19 } from "nostr-tools";
|
||||
@ -38,6 +39,7 @@ import { logError, logInfo, logWarning } from "../lib/logger";
|
||||
import { useToastContext } from "../components/Toaster/Toaster";
|
||||
import { useIntl } from "@cookbook/solid-intl";
|
||||
import { account as tAccount } from "../translations";
|
||||
import { getMembershipStatus } from "../lib/membership";
|
||||
|
||||
export type AccountContextStore = {
|
||||
likes: string[],
|
||||
@ -68,6 +70,7 @@ export type AccountContextStore = {
|
||||
showGettingStarted: boolean,
|
||||
showLogin: boolean,
|
||||
emojiHistory: EmojiOption[],
|
||||
membershipStatus: MembershipStatus,
|
||||
actions: {
|
||||
showNewNoteForm: () => void,
|
||||
hideNewNoteForm: () => void,
|
||||
@ -126,6 +129,7 @@ const initialData = {
|
||||
showGettingStarted: false,
|
||||
showLogin: false,
|
||||
emojiHistory: [],
|
||||
membershipStatus: {},
|
||||
};
|
||||
|
||||
export const AccountContext = createContext<AccountContextStore>();
|
||||
@ -143,6 +147,8 @@ export function AccountProvider(props: { children: JSXElement }) {
|
||||
|
||||
let connectedRelaysCopy: Relay[] = [];
|
||||
|
||||
let membershipSocket: WebSocket | undefined;
|
||||
|
||||
onMount(() => {
|
||||
setInterval(() => {
|
||||
checkNostrChange();
|
||||
@ -185,6 +191,49 @@ export function AccountProvider(props: { children: JSXElement }) {
|
||||
}
|
||||
};
|
||||
|
||||
const openMembershipSocket = (onOpen: () => void) => {
|
||||
membershipSocket = new WebSocket('wss://wallet.primal.net/v1');
|
||||
|
||||
membershipSocket.addEventListener('close', () => {
|
||||
console.log('PREMIUM SOCKET CLOSED');
|
||||
});
|
||||
|
||||
membershipSocket.addEventListener('open', () => {
|
||||
console.log('PREMIUM SOCKET OPENED');
|
||||
onOpen();
|
||||
});
|
||||
}
|
||||
|
||||
const checkMembershipStatus = () => {
|
||||
openMembershipSocket(() => {
|
||||
if (!membershipSocket || membershipSocket.readyState !== WebSocket.OPEN) return;
|
||||
|
||||
const subId = `ps_${APP_ID}`;
|
||||
|
||||
let gotEvent = false;
|
||||
|
||||
const unsub = subTo(membershipSocket, subId, (type, _, content) => {
|
||||
if (type === 'EVENT') {
|
||||
const status: MembershipStatus = JSON.parse(content?.content || '{}');
|
||||
|
||||
gotEvent = true;
|
||||
updateStore('membershipStatus', () => ({ ...status }));
|
||||
}
|
||||
|
||||
if (type === 'EOSE') {
|
||||
unsub();
|
||||
membershipSocket?.close();
|
||||
|
||||
if (!gotEvent) {
|
||||
updateStore('membershipStatus', () => ({ tier: 'none' }));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getMembershipStatus(store.publicKey, subId, membershipSocket);
|
||||
});
|
||||
};
|
||||
|
||||
const showGetStarted = () => {
|
||||
updateStore('showGettingStarted', () => true);
|
||||
}
|
||||
@ -227,8 +276,17 @@ export function AccountProvider(props: { children: JSXElement }) {
|
||||
}
|
||||
|
||||
const setPublicKey = (pubkey: string | undefined) => {
|
||||
|
||||
if(pubkey && pubkey.length > 0) {
|
||||
updateStore('publicKey', () => pubkey);
|
||||
pubkey ? localStorage.setItem('pubkey', pubkey) : localStorage.removeItem('pubkey');
|
||||
localStorage.setItem('pubkey', pubkey);
|
||||
checkMembershipStatus();
|
||||
}
|
||||
else {
|
||||
updateStore('publicKey', () => undefined);
|
||||
localStorage.removeItem('pubkey');
|
||||
}
|
||||
|
||||
updateStore('isKeyLookupDone', () => true);
|
||||
};
|
||||
|
||||
|
38
src/lib/membership.ts
Normal file
38
src/lib/membership.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Kind } from "../constants";
|
||||
import { signEvent } from "./nostrAPI";
|
||||
|
||||
export const getMembershipStatus = async (pubkey: string | undefined, subId: string, socket: WebSocket) => {
|
||||
if (!pubkey) return;
|
||||
|
||||
const event = {
|
||||
kind: Kind.Settings,
|
||||
tags: [['p', pubkey]],
|
||||
created_at: Math.floor((new Date()).getTime() / 1000),
|
||||
content: JSON.stringify({}),
|
||||
};
|
||||
|
||||
try {
|
||||
const signedNote = await signEvent(event);
|
||||
|
||||
const message = JSON.stringify([
|
||||
"REQ",
|
||||
subId,
|
||||
{cache: ["membership_status", { event_from_user: signedNote }]},
|
||||
]);
|
||||
|
||||
if (socket) {
|
||||
const e = new CustomEvent('send', { detail: { message, ws: socket }});
|
||||
|
||||
socket.send(message);
|
||||
socket.dispatchEvent(e);
|
||||
} else {
|
||||
throw('no_socket');
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
} catch (reason) {
|
||||
console.error('Failed to upload: ', reason);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -124,3 +124,21 @@ export const subscribeTo = (subId: string, cb: (type: NostrEventType, subId: str
|
||||
socket()?.removeEventListener('message', listener);
|
||||
};
|
||||
};
|
||||
|
||||
export const subTo = (socket: WebSocket, subId: string, cb: (type: NostrEventType, subId: string, content?: NostrEventContent) => void ) => {
|
||||
const listener = (event: MessageEvent) => {
|
||||
const message: NostrEvent | NostrEOSE = JSON.parse(event.data);
|
||||
const [type, subscriptionId, content] = message;
|
||||
|
||||
if (subId === subscriptionId) {
|
||||
cb(type, subscriptionId, content);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
socket.addEventListener('message', listener);
|
||||
|
||||
return () => {
|
||||
socket.removeEventListener('message', listener);
|
||||
};
|
||||
};
|
||||
|
12
src/types/primal.d.ts
vendored
12
src/types/primal.d.ts
vendored
@ -663,3 +663,15 @@ export type ContactsData = {
|
||||
tags: string[][],
|
||||
following: string[],
|
||||
}
|
||||
|
||||
export type MembershipStatus = {
|
||||
pubkey?: string,
|
||||
tier?: string,
|
||||
name?: string,
|
||||
rename?: string,
|
||||
nostr_address?: string,
|
||||
lightning_address?: string,
|
||||
primal_vip_profile?: string,
|
||||
used_storage?: number,
|
||||
expires_on?: number,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user