Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom channels #13

Merged
merged 7 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"linkifyjs": "^4.1.1",
"lodash.debounce": "^4.0.8",
"lodash.find": "^4.6.0",
"lodash.findindex": "^4.6.0",
"lodash.get": "^4.4.2",
"lodash.includes": "^4.3.0",
"lodash.isempty": "^4.4.0",
Expand Down Expand Up @@ -98,6 +99,7 @@
"@tauri-apps/cli": "^1.4",
"@types/lodash.debounce": "^4.0.7",
"@types/lodash.find": "^4.6.7",
"@types/lodash.findindex": "^4.6.7",
"@types/lodash.get": "^4.4.7",
"@types/lodash.includes": "^4.3.7",
"@types/lodash.isempty": "^4.4.7",
Expand Down
4 changes: 2 additions & 2 deletions src/common/components/CastRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const renderLink = ({ attributes, content }) => {


export const CastRow = ({ cast, isSelected, showChannel, onSelect, channels, isThreadView = false }: CastRowProps) => {
// if (isSelected) console.log(cast.embeds);
// if (isSelected) console.log(cast);

const { accounts, selectedAccountIdx } = useAccountStore();
const [didLike, setDidLike] = useState(false)
Expand Down Expand Up @@ -92,7 +92,7 @@ export const CastRow = ({ cast, isSelected, showChannel, onSelect, channels, isT
}, { enabled: isSelected }, [isSelected, selectedAccountIdx, authorFid, cast.hash, reactions.recasts]);

const getChannelForParentUrl = (parentUrl: string | null): ChannelType | undefined => parentUrl ?
channels.find((channel) => channel.parent_url === parentUrl) : undefined;
channels.find((channel) => channel.url === parentUrl) : undefined;

const getIconForCastReactionType = (reactionType: CastReactionType, isActive?: boolean): JSX.Element | undefined => {
const className = classNames(isActive ? "text-gray-300" : "", "mt-0.5 w-4 h-4 mr-1");
Expand Down
7 changes: 3 additions & 4 deletions src/common/components/CastThreadView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export const CastThreadView = ({ cast, onBack, fid, isActive }: CastThreadViewPr
const draftIdx = useNewPostStore(state => state.drafts && state.drafts.findIndex(draft => draft.parentCastId?.hash === cast?.hash));

const {
channels,
selectedChannelIdx
allChannels: channels,
selectedChannelUrl
} = useAccountStore();

const {
Expand Down Expand Up @@ -105,10 +105,9 @@ export const CastThreadView = ({ cast, onBack, fid, isActive }: CastThreadViewPr
<CastRow
cast={cast}
channels={channels}
showChannel={selectedChannelIdx === null}
showChannel={selectedChannelUrl === null}
isSelected={selectedCastIdx === idx}
isThreadView
showEmbed
/>
</div>
</>
Expand Down
24 changes: 15 additions & 9 deletions src/common/components/CommandPalette/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { Fragment, useState } from 'react';
import { channels } from "@/common/constants/channels";
import { CommandType } from "@/common/constants/commands";
import { classNames } from "@/common/helpers/css";
import { accountCommands, channelCommands, useAccountStore } from '@/stores/useAccountStore';
import { useNavigationStore } from "@/stores/useNavigationStore";
import { newPostCommands } from "@/stores/useNewPostStore";
import { Combobox, Dialog, Transition } from '@headlessui/react';
import { MagnifyingGlassIcon } from '@heroicons/react/20/solid';
import { BellIcon, FaceSmileIcon, PlusCircleIcon } from '@heroicons/react/24/outline';
import { MagnifyingGlassIcon, RectangleGroupIcon } from '@heroicons/react/20/solid';
import { BellIcon, FaceSmileIcon } from '@heroicons/react/24/outline';
import commandScore from "command-score";
import { useHotkeys } from 'react-hotkeys-hook';
import { useNavigate } from "react-router-dom";
Expand Down Expand Up @@ -42,6 +41,14 @@ export const getNavigationCommands = (navigate?: (path: string) => void | null):
enableOnFormTags: false,
action: () => navigate && navigate('/search'),
},
{
name: 'Switch to Channels',
aliases: ['channels',],
icon: RectangleGroupIcon,
shortcut: 'shift+c',
enableOnFormTags: false,
action: () => navigate && navigate('/channels'),
},
{
name: 'Notifications',
aliases: ['notify', 'alert', 'mentions', 'replies', 'messages', 'inbox',],
Expand Down Expand Up @@ -72,7 +79,8 @@ export default function CommandPalette() {
} = useNavigationStore();

const {
setCurrentChannelIdx
setCurrentChannelIdx,
allChannels
} = useAccountStore();

useHotkeys(['meta+k'], () => {
Expand Down Expand Up @@ -109,7 +117,7 @@ export default function CommandPalette() {
}

const nonHotkeyCommands: CommandType[] = [];
channels.map((c) => c.name).map((channelName: string, idx: number) => {
allChannels.map((c) => c.name).map((channelName: string, idx: number) => {
nonHotkeyCommands.push({
name: channelName,
action: () => {
Expand Down Expand Up @@ -246,11 +254,9 @@ export default function CommandPalette() {
/>}
<span className="ml-3 flex-auto truncate">
{action.name}
{/* {action.score && `(${action.score})`} */}
</span>
<span className="ml-3 flex-none text-xs font-semibold text-gray-400">
{/* <kbd className="font-sans">⌘</kbd> */}
<kbd className="font-sans">{action.shortcut}</kbd>
<span className="ml-3 flex-none text-xs text-gray-200 bg-gray-600 px-2 py-1 rounded-md">
<kbd className="font-mono">{action.shortcut}</kbd>
</span>
</>
)}
Expand Down
17 changes: 9 additions & 8 deletions src/common/components/NewPostEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { classNames } from "@/common/helpers/css";
import { NewPostDraft, useNewPostStore } from "@/stores/useNewPostStore";
import { useAccountStore } from "@/stores/useAccountStore";
import { Listbox, Transition, Combobox } from '@headlessui/react'
import { ChannelType, channels } from "@/common/constants/channels";
import { ChannelType } from "@/common/constants/channels";
import isEmpty from "lodash.isempty";
import { DraftStatus, DraftType } from "../constants/farcaster";
import { CasterType, getNeynarUserSearchEndpoint } from "../helpers/neynar";
Expand Down Expand Up @@ -69,12 +69,14 @@ export default function NewPostEntry({ draftIdx, onPost, hideChannel }: NewPostE
const textareaRef = useRef<HTMLTextAreaElement>(null);
const textareaElement = textareaRef.current;
const {
selectedChannelIdx
selectedChannelUrl,
allChannels: channels,
} = useAccountStore();


const account = useAccountStore((state) => state.accounts[state.selectedAccountIdx]);
const hasMultipleAccounts = useAccountStore((state) => state.accounts.length > 1);
const channel = channels.find((channel: ChannelType) => channel.parent_url === draft?.parentUrl);
const channel = channels.find((channel: ChannelType) => channel.url === draft?.parentUrl);
const isReply = draft?.parentCastId !== undefined;

const onChange = (cast: DraftType) => {
Expand Down Expand Up @@ -114,8 +116,8 @@ export default function NewPostEntry({ draftIdx, onPost, hideChannel }: NewPostE
};

useEffect(() => {
onChange({ ...draft, parentUrl: selectedChannelIdx !== null ? channels[selectedChannelIdx].parent_url : undefined });
}, [selectedChannelIdx])
onChange({ ...draft, parentUrl: selectedChannelUrl || undefined });
}, [selectedChannelUrl])

useEffect(() => {
if (textareaElement) {
Expand All @@ -141,15 +143,14 @@ export default function NewPostEntry({ draftIdx, onPost, hideChannel }: NewPostE
"@": {
dataProvider: (token: string) => {
return findUsername(token.toLowerCase());
// return await findUsername(token.toLowerCase()).slice(0, 7)
},
component: MentionDropdownItem,
output: (item, trigger) => `@${item.username}`
}
}

const onUpdateParentUrl = (channel: ChannelType) => {
const newParentUrl = (channel.parent_url === draft.parentUrl) ? undefined : channel.parent_url;
const newParentUrl = (channel.url === draft.parentUrl) ? undefined : channel.url;
onChange({ ...draft, parentUrl: newParentUrl })
}

Expand Down Expand Up @@ -264,7 +265,7 @@ export default function NewPostEntry({ draftIdx, onPost, hideChannel }: NewPostE
<Listbox.Options className="absolute left-0 z-100 mt-1 max-h-56 w-42 overflow-auto rounded-sm bg-radix-slate10 text-base shadow ring-1 ring-gray-900 focus:outline-none sm:text-sm">
{channels.map((channel) => (
<Listbox.Option
key={channel.parent_url}
key={channel.url}
className={({ active }) =>
classNames(
active ? 'text-gray-200 bg-gray-600' : 'text-gray-300 bg-gray-700',
Expand Down
49 changes: 23 additions & 26 deletions src/common/components/RightSidebar/ChannelsOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,58 @@
import React, { useState } from "react";
import React from "react";
import { ChannelType } from "@/common/constants/channels";
import { classNames } from "@/common/helpers/css";
import { useAccountStore } from "@/stores/useAccountStore";
import { SidebarHeader } from "./SidebarHeader";
import { useNavigate } from "react-router-dom";

const ChannelsOverview = () => {
const [showAll, setShowAll] = useState(false);
const MAX_SIDEBAR_CHANNELS = 9;
const navigate = useNavigate();
const {
channels,
selectedChannelIdx,
setCurrentChannelIdx
selectedChannelUrl,
setSelectedChannelUrl,
resetSelectedChannel
} = useAccountStore();

const onToggleShowAll = () => {
setShowAll(!showAll);
}

const sidebarChannels = showAll ? channels : channels.slice(0, MAX_SIDEBAR_CHANNELS);
if (!showAll && selectedChannelIdx && selectedChannelIdx >= MAX_SIDEBAR_CHANNELS) {
sidebarChannels.push(channels[selectedChannelIdx]);
}
let channels: ChannelType[] = useAccountStore((state) => state.accounts[state.selectedAccountIdx]?.channels);
if (!channels) channels = [];

return (<>
<SidebarHeader title="Channels" actionTitle={showAll ? 'Show less' : 'Show all'} onClick={onToggleShowAll} />
<SidebarHeader title="Channels" actionTitle={'Manage'} onClick={() => navigate('/channels')} />
<ul role="list" className="mx-4 m-4">
<li key="follow-feed" className="px-2 sm:px-3 lg:px-4">
<li key="follow-feed" className="px-2 sm:px-3 lg:px-6">
<span
onClick={() => setCurrentChannelIdx(null)}
onClick={() => resetSelectedChannel()}
className={classNames(
selectedChannelIdx === null
selectedChannelUrl === ''
? 'bg-gray-800 text-gray-100'
: 'text-gray-400 hover:text-gray-100 hover:bg-gray-800',
'group align-center justify-between flex gap-x-3 rounded-md p-1 text-sm leading-6 cursor-pointer'
)}
>
<span className="font-normal truncate">Follow feed</span>
<span className="font-normal truncate">Feed</span>
<kbd className="px-1.5 py-1 text-xs border rounded-md bg-gray-700 text-gray-300 border-gray-600">
Shift + 0
</kbd>
</span>
</li>
{(sidebarChannels).map((channel: ChannelType, idx: number) => (
<li key={channel.name} className="px-2 sm:px-3 lg:px-4">
{channels.map((channel: ChannelType, idx: number) => (
<li key={channel.name} className="px-2 sm:px-3 lg:px-6">
<div
onClick={() => setCurrentChannelIdx(idx)}
onClick={() => setSelectedChannelUrl(channel.url)}
className={classNames(
selectedChannelIdx === idx
selectedChannelUrl === channel.url
? 'text-white font-semibold'
: 'text-gray-400 hover:text-white',
'flex align-center justify-between flex gap-x-3 rounded-md p-1 text-sm leading-6 cursor-pointer'
'flex align-center justify-between gap-x-3 rounded-md p-1 text-sm leading-6 cursor-pointer'
)}
>
<span className="font-normal truncate">{channel.name}</span>
<div className="flex">
{channel.icon_url && (<img src={channel.icon_url} alt="" className="-ml-7 mr-2 mt-0.5 border border-gray-400 h-5 w-5 flex-none rounded-full bg-gray-800" />)}
<span className="font-normal truncate">{channel.name}</span>
</div>
{idx < 9 && (
<kbd className="px-1.5 py-0.5 text-xs border rounded-md bg-gray-700 text-gray-300 border-gray-600">
Shift + {idx + 1}
{idx + 1}
</kbd>
)}
</div>
Expand Down
59 changes: 59 additions & 0 deletions src/common/components/Toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { Switch } from '@headlessui/react'
import { classNames } from '../helpers/css';


type ToggleProps = {
enabled: boolean;
setEnabled: (enabled: boolean) => void;
}
export default function Toggle({ enabled, setEnabled }: ToggleProps) {

return (
<Switch
checked={enabled}
onChange={setEnabled}
className={classNames(
enabled ? 'bg-green-600' : 'bg-gray-200',
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none'
)}
>
<span className="sr-only">Activate</span>
<span
className={classNames(
enabled ? 'translate-x-5' : 'translate-x-0',
'pointer-events-none relative inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out'
)}
>
<span
className={classNames(
enabled ? 'opacity-0 duration-100 ease-out' : 'opacity-100 duration-200 ease-in',
'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity'
)}
aria-hidden="true"
>
{/* <svg className="h-3 w-3 text-gray-400" fill="none" viewBox="0 0 12 12">
<path
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg> */}
</span>
<span
className={classNames(
enabled ? 'opacity-100 duration-200 ease-in' : 'opacity-0 duration-100 ease-out',
'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity'
)}
aria-hidden="true"
>
<svg className="h-3 w-3 text-green-600" fill="currentColor" viewBox="0 0 12 12">
<path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z" />
</svg>
</span>
</span>
</Switch>
)
}
Loading