Skip to content

Commit

Permalink
improve onboarding, allow removing accounts, improve settings
Browse files Browse the repository at this point in the history
  • Loading branch information
hellno committed Sep 12, 2023
1 parent 5423377 commit 3367346
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 43 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@headlessui/react": "^1.7.16",
"@heroicons/react": "^2.0.18",
"@radix-ui/colors": "^2.1.0",
"@radix-ui/react-alert-dialog": "^1.0.4",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-toast": "^1.1.4",
"@radix-ui/react-tooltip": "^1.0.6",
Expand Down
42 changes: 42 additions & 0 deletions src/common/components/AlertDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import * as AlertDialog from '@radix-ui/react-alert-dialog';

type AlertDialogProps = {
buttonText: string;
onClick: () => void;
};

const AlertDialogDemo = ({ buttonText, onClick }: AlertDialogProps) => (
<AlertDialog.Root>
<AlertDialog.Trigger asChild>
<button className="text-gray-100 bg-gray-600 hover:bg-gray-500 inline-flex h-[35px] items-center justify-center rounded-sm px-[15px] font-medium leading-none outline-none focus:bg-gray-500">
{buttonText}
</button>
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Overlay className="bg-[#FFFFFFAF] data-[state=open]:animate-overlayShow fixed inset-0" />
<AlertDialog.Content className="data-[state=open]:animate-contentShow fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[500px] translate-x-[-50%] translate-y-[-50%] rounded-[6px] bg-radix-slate10 p-[25px] shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] focus:outline-none">
<AlertDialog.Title className="text-radix-mauve1 m-0 text-[20px] font-medium">
Are you absolutely sure?
</AlertDialog.Title>
<AlertDialog.Description className="text-radix-mauve1 mt-4 mb-5 text-[15px] leading-normal">
This action cannot be undone. <br />This will disconnect your account from herocast.
</AlertDialog.Description>
<div className="flex justify-end gap-[25px]">
<AlertDialog.Cancel asChild>
<button className="text-radix-mauve11 bg-radix-mauve7 hover:bg-radix-mauve6 focus:shadow-radix-mauve9 inline-flex h-[35px] items-center justify-center rounded-sm px-[15px] font-medium leading-none outline-none focus:shadow-[0_0_0_2px]">
Cancel
</button>
</AlertDialog.Cancel>
<AlertDialog.Action asChild onClick={() => onClick()}>
<button className="text-radix-red3 bg-radix-red9 hover:bg-radix-red10 focus:shadow-radix-red7 inline-flex h-[35px] items-center justify-center rounded-sm px-[15px] font-medium leading-none outline-none focus:shadow-[0_0_0_2px]">
Yes, disconnect account
</button>
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
);

export default AlertDialogDemo;
2 changes: 1 addition & 1 deletion src/common/components/FarcasterLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ const FarcasterLogin = () => {
onClick={() => onCreateNewAccount()}
className="relative -ml-px inline-flex items-center gap-x-1.5 rounded-r-sm px-3 py-2 text-sm bg-gray-700 font-semibold text-gray-200 ring-1 ring-inset ring-gray-500 hover:bg-gray-600"
>
Add account
Connect account
</button>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/common/components/RightSidebar/AccountsRightSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ const AccountsRightSidebar = ({ showChannels }: AccountsRightSidebarProps) => {
const renderEmptyState = () => (
<div className="ml-6">
<EmptyStateWithAction
title="No accounts"
description="Add an account to get started"
title="Connect Farcaster accounts"
description="Get started with herocast"
onClick={() => navigate('/accounts')}
submitText="Add account"
submitText="Connect account"
icon={UserPlusIcon}
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Accounts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function Accounts() {
<div className="flex min-w-full flex-col">
<div>
<h1 className="mb-4 text-lg font-bold tracking-tight text-gray-200 sm:text-4xl">
Add Farcaster account
Connect Farcaster accounts
</h1>
<FarcasterLogin />
{/*
Expand Down
73 changes: 62 additions & 11 deletions src/pages/Feed/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { CastThreadView } from "@/common/components/CastThreadView";
import { getNeynarFeedEndpoint } from "@/common/helpers/neynar";
import { Loading } from "@/common/components/Loading";
import EmptyStateWithAction from "@/common/components/EmptyStateWithAction";
import { UserPlusIcon } from "@heroicons/react/24/outline";
import { ChevronRightIcon, UserPlusIcon } from "@heroicons/react/24/outline";
import { useNavigate } from "react-router-dom";
import { SelectableListWithHotkeys } from "@/common/components/SelectableListWithHotkeys";
import { Key } from "ts-key-enum";
Expand All @@ -33,10 +33,13 @@ export default function Feed() {
accounts,
channels,
selectedAccountIdx,
selectedChannelIdx
selectedChannelIdx,
hydrated
} = useAccountStore();

const isHydrated = useAccountStore(state => state._hydrated);
// const isHydrated = useAccountStore(state => state._hydrated);

console.log('hydrated', hydrated, 'accountStoreState', useAccountStore(state => state));

const selectedChannelParentUrl = channels && selectedChannelIdx !== null ? channels[selectedChannelIdx].parent_url : undefined;
const account: AccountObjectType = accounts[selectedAccountIdx];
Expand Down Expand Up @@ -163,16 +166,64 @@ export default function Feed() {


const renderEmptyState = () => (
<EmptyStateWithAction
title="No accounts"
description="Add an account to get started"
onClick={() => navigate('/accounts')}
submitText="Add account"
icon={UserPlusIcon}
/>
<>
<div className="max-w-7xl px-6 pb-24 sm:pb-32 lg:flex lg:px-8">
<div className="mx-auto max-w-2xl flex-shrink-0 lg:mx-0 lg:max-w-xl">
<div className="mt-12">
<a href="https://paragraph.xyz/@hellno/herocast-log-nr2" target="_blank" rel="noreferrer"
className="inline-flex space-x-6">
<span className="rounded-full bg-green-700/10 px-3 py-1 text-sm font-semibold leading-6 text-green-400 ring-1 ring-inset ring-green-700/20">
What&apos;s new
</span>
<span className="inline-flex items-center space-x-2 text-sm font-medium leading-6 text-gray-300">
<span>Just shipped an update for you</span>
<ChevronRightIcon className="h-5 w-5 text-gray-500" aria-hidden="true" />
</span>
</a>
</div>
<h1 className="mt-10 text-4xl font-bold tracking-tight text-white sm:text-6xl">
Welcome to herocast
</h1>
<p className="mt-6 text-lg leading-8 text-gray-300">
herocast is a desktop Farcaster client for power users aka superhuman for Farcaster. <br /><br />
It has support for multiple accounts and can switch channels faster than you can say &apos;Memes&apos;. It supports{' '}
<kbd className="px-1.5 py-1 text-xs border rounded-md bg-gray-700 text-gray-300 border-gray-600">
Cmd + K
</kbd> (command palette) to control everything.
You can navigate with <kbd className="px-1.5 py-1 text-xs border rounded-md bg-gray-700 text-gray-300 border-gray-600">
j
</kbd> and <kbd className="px-1.5 py-1 text-xs border rounded-md bg-gray-700 text-gray-300 border-gray-600">
k
</kbd>through all lists, <kbd className="px-1.5 py-1 text-xs border rounded-md bg-gray-700 text-gray-300 border-gray-600">
l
</kbd> to like (lowercase L) and <kbd className="px-1.5 py-1 text-xs border rounded-md bg-gray-700 text-gray-300 border-gray-600">
r
</kbd> to recast. Switch channels on Feed page with <kbd className="px-1.5 py-1 text-xs border rounded-md bg-gray-700 text-gray-300 border-gray-600">
Shift + 1 to 9
</kbd>. Open external links in a cast with <kbd className="px-1.5 py-1 text-xs border rounded-md bg-gray-700 text-gray-300 border-gray-600">
Shift + o
</kbd>.
</p>
<div className="mt-10 flex items-center gap-x-6">
<button
onClick={() => navigate('/accounts')}
className="flex rounded-sm bg-green-700 px-5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-green-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-400"
>
Get started <UserPlusIcon className="ml-2 h-5 w-5 text-gray-100" aria-hidden="true" />
</button>
<a href="https://paragraph.xyz/@hellno/herocast-log-nr2" target="_blank" rel="noreferrer"
className="rounded-sm px-3.5 py-2 text-sm font-semibold leading-6 text-white outline outline-gray-500">
Learn more <span aria-hidden="true"></span>
</a>
</div>
</div>
</div>
</>
)

return isHydrated && isEmpty(accounts) ? renderEmptyState() : (

console.log('hydrated', hydrated, 'isEmpty', isEmpty(accounts))
return hydrated && isEmpty(accounts) ? renderEmptyState() : (
<div className="min-w-full mr-4">
{showCastThreadView ? renderThread() : renderFeed()}
{isLoadingFeed && <Loading />}
Expand Down
62 changes: 49 additions & 13 deletions src/pages/Settings/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import AlertDialogDemo from "@/common/components/AlertDialog";
import { getNavigationCommands } from "@/common/components/CommandPalette";
import { classNames } from "@/common/helpers/css";
import { supabaseClient } from "@/common/helpers/supabase";
import { accountCommands, channelCommands, useAccountStore } from "@/stores/useAccountStore";
import { AccountObjectType, accountCommands, channelCommands, useAccountStore } from "@/stores/useAccountStore";
import { newPostCommands } from "@/stores/useNewPostStore";
import { User } from "@supabase/supabase-js";
import React, { useEffect, useState } from "react";
Expand All @@ -16,9 +18,13 @@ export default function Settings() {
const [user, setUser] = useState<User | null>(null)

const {
resetStore
accounts,
resetStore,
removeAccount
} = useAccountStore();



useEffect(() => {
const getUser = async () => {
const { data: { user } } = await supabaseClient.auth.getUser();
Expand Down Expand Up @@ -60,7 +66,10 @@ export default function Settings() {
...channelCommands,
];

return (<div className="overflow-hidden shadow sm:rounded-lg">
return (<div className="mt-20 overflow-hidden">
<div className="border-b border-gray-200">
<h1 className="text-xl font-semibold leading-7 text-gray-100">Hotkeys / Keyboard Shortcuts</h1>
</div>
<div className="px-2 py-4">
<h3 className="text-base font-semibold leading-7 text-gray-100">hotkeys overview</h3>
<p className="mt-1 max-w-2xl text-sm leading-6 text-gray-400">list of all hotkeys in herocast</p>
Expand Down Expand Up @@ -88,24 +97,51 @@ export default function Settings() {

return (
<div className="flex flex-col space-y-4">
<div className="flex flex-row mb-4">
<span className="text-sm font-semibold text-gray-400 mr-2">User</span>
<span className="text-sm font-semibold text-white">{displayEmail}</span>
<div className="border-b border-gray-200">
<h1 className="text-xl font-semibold leading-7 text-gray-100">Herocast account</h1>
</div>
<div className="flex flex-row mt-4 px-2">
<span className="text-sm font-semibold text-gray-100 mr-2">Email</span>
<span className="text-sm font-semibold text-gray-400 ">{displayEmail}</span>
</div>
<button
type="button"
onClick={() => onUpdateAccountStatus()}
className="w-48 inline-flex items-center rounded-sm bg-gray-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
onClick={() => onLogout()}
className="w-20 inline-flex items-center rounded-sm bg-gray-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
>
Update Account Status
Logout
</button>
<button
{/* <button
type="button"
onClick={() => onLogout()}
onClick={() => onUpdateAccountStatus()}
className="w-48 inline-flex items-center rounded-sm bg-gray-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
>
Logout
</button>
Update Account Status
</button> */}
<div className="border-b border-gray-200">
<h1 className="text-xl font-semibold leading-7 text-gray-100">Farcaster accounts</h1>
</div>
<ul role="list" className="divide-y divide-white/5">
{accounts.map((item: AccountObjectType, idx: number) => (
<li key={item.id} className="px-2 py-2">
<div
className="flex items-center gap-x-3"
>
{/* <img src={item.user.imageUrl} alt="" className="h-6 w-6 flex-none rounded-full bg-gray-800" /> */}
<h3 className={classNames(
"text-gray-100",
"flex-auto truncate text-sm font-semibold leading-6")}>{item.name}</h3>
<span className="text-gray-400">{item.status}</span>
{item.platformAccountId && item.status === 'active' && (
<p className="truncate text-sm text-gray-500">
fid {item.platformAccountId}
</p>
)}
<AlertDialogDemo buttonText={`Disconnect`} onClick={() => removeAccount(idx)} />
</div>
</li>
))}
</ul>
{renderInfoSection()}
</div>
)
Expand Down
28 changes: 15 additions & 13 deletions src/stores/useAccountStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface AccountStoreProps {
selectedChannelIdx: number | null;
accounts: AccountObjectType[];
channels: ChannelType[];
_hydrated: boolean;
hydrated: boolean;
}

interface AccountStoreActions {
Expand All @@ -47,7 +47,7 @@ const initialState: AccountStoreProps = {
channels: [],
selectedAccountIdx: 0,
selectedChannelIdx: null,
_hydrated: false,
hydrated: false,
};

export const mutative = (config) =>
Expand Down Expand Up @@ -108,7 +108,7 @@ const store = (set: StoreSet) => ({
set((state) => {
supabaseClient
.from('accounts')
.delete()
.update({ status: AccountStatusType.removed })
.eq('id', state.accounts[idx].id)
.select()
.then(({ error, data }) => {
Expand Down Expand Up @@ -155,19 +155,20 @@ export const hydrate = async () => {

const { data: accountData, error: accountError } = await supabaseClient
.from('decrypted_accounts')
// .from('accounts')
.select('*')
.eq('user_id', user?.id)
.neq('status', AccountStatusType.removed)

if (accountError) {
console.error('error hydrating account store', accountError);
return;
}

let accountsForState: AccountObjectType[] = [];
if (accountData.length === 0) {
console.log('no accounts found');
} else {
const accountsForState: AccountObjectType[] = accountData.map((account) => ({
accountsForState = accountData.map((account) => ({
id: account.id,
name: account.name,
status: account.status,
Expand All @@ -179,18 +180,17 @@ export const hydrate = async () => {
privateKey: account.decrypted_private_key,
}))

useAccountStore.setState({
accounts: accountsForState,
channels: channels,
selectedAccountIdx: 0,
_hydrated: true
});
}

useAccountStore.setState({
accounts: accountsForState,
channels: channels,
selectedAccountIdx: 0,
hydrated: true
});
console.log('done hydrating account store')
}

hydrate();

const switchAccountTo = (idx: number) => {
if (idx < 0) return;

Expand Down Expand Up @@ -295,3 +295,5 @@ const getChannelCommands = () => {

export const accountCommands = getAccountCommands();
export const channelCommands = getChannelCommands();

hydrate();
2 changes: 1 addition & 1 deletion tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module.exports = {
plugins: [
require('@tailwindcss/forms'),
require('radix-colors-for-tailwind')({
colors: ["slate", "blue"],
colors: ["slate", "blue", "red", "mauve",],
}),
],
};

0 comments on commit 3367346

Please sign in to comment.