-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added base POC to listen for push notifications on a service-worker; * Base upload and fetch updates for multi payload with default key; * Fixed FileProvider with better handling of the new result structure; Fixed useSiteData with correct fallback if static files fail; * Cleaned up the uploadFileMetaData additionalThumbnails; * Cleaned up uploadFile with multi-payloads and only support for Blobs; * Fixed thumbs; * Clean out old fileMetaData properties; * Updated getting thumb and payload to use the payloadKey; Updated uploads to support payloads with corresponding thumbs; * Fixes and updates on new thumb approach; * Made contacts multi payload; * Updated fileBrowser to support downloading the first payload image; * In Progress migration of attributes to use multi payload; * In Progress migration of attributes to use multi payload; * Fixes and updates to have the attributes work with their multi payload data * Contact source fixes; * Cleanup imports; * Expanded useTinyThumb with Key to get the right thumbnail sizes from the meta * Improved attribute editor; Fixed imageSelector to show new Blob when image selected; * Added base multi payload support to the posts; * Updated article composer; And UI components to be compatible with the new hooks * TODO: Fixes; * Added support for the media with an embeddedPost; * Added multi payload for comments; * Added multi payload for comments; * Extra null checks on the serverMetadata; * Fixed multi payload multi files previewThumbnail fetching; * Add support for editing a post; * Got base articleComposer working again; * Got base articleComposer working again; * Added support for using the updatePost from savePost; Added adding and/or removing payloads on the updatePost handler; * Better post update and save handling; * Better post update and save handling; * Improved dynamic data fetching error "handling"; * Better fileBrowser; * Better fileBrowser; * Added new byGlobalTransitId functions * Improved OdinImage structure; * Made the images work again over transit when on the feed; * Made the images work again over transit when on the feed; * More null checks for a potentially null ServermetaData; * Fixed processing state updates; * Added upload state for media when already in the SocialFeed; * Added CommentMedia preview; * Build fixes; * Updated comment media teaser; * Added systemFileType support to OdinImage; Fixed Embedded Post with globalTransitId; * Updated static file handling with default payloads being included; * Removed wrong/old useage of DEFAULT_PAYLOAD_KEY; * Added a "Show on your feed" checkbox for channels; * Fixed external Files from the post to still work; * Made usePost always use save function; * Made articles have their payloads on the postFile; * Added lastModified into the OdinImage props for cache busting; * Added apex domain info on DNS Settings View; * Unified asserts on file providers; * Added lastModified into the OdinImage props for cache busting; * Cleanup YourInfo & YourSignature; * Updated ProfileCardProvider to avoid a 404 on a thumb request when the profile image is an SVG; * Improved svg handling on OdinImage * Added video's into the PostFile payloads; * Add support for big articles on updatePost; * Made saveAttr update header/append payloads instead of always download and re-uploading payloads; * Improved attribute upload handling; * Moved push notification management into a hook with an owner specific PushProvider; * Added notification devices settings view; * Fix deps; * Improved attribute file type; * Refactored AttributeFile to DriveSearchResult type; * Clearly indicate when changing encryption state fails; * Add support for re-uploading attribute when their encrypt / unencrypt changes; * Reset usage to senderOdinId on Feed Drive posts; * Changed dependency location; * Changed dependency location for some more; * Fixed YourSignature error; * Refactored PostFile<T> to DriveSearchResult<T> * Import fix; * Bumped version of libs; Added extra trigger for the multi-payload branch; * Cleanup unused deps; * Bump lib version(s) * Added extra check on the contact source provider for undefined attr; * Fixes for non-existent contentType; * Updated endpoint used to fetch the application server key; * Addec chat-app; * Reorganized types; * Fiex isExternal check on PostTeaserCard; * Simplified upload file objects; Added base chat provider implementations; Base UI work for a chat screen; * Bump lib version(s) * Updated sw; * WIP POC; * Fixed base64ToUint8Array; * WIP POC; --------- Co-authored-by: github-actions <[email protected]>
- Loading branch information
1 parent
f043be7
commit eb20ca4
Showing
14 changed files
with
3,655 additions
and
239 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
153 changes: 153 additions & 0 deletions
153
packages/chat-app/src/templates/Conversations/Conversations.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import { | ||
ActionButton, | ||
ConnectionImage, | ||
ConnectionName, | ||
EmojiSelector, | ||
FileSelector, | ||
ImageIcon, | ||
Input, | ||
OwnerName, | ||
VolatileInput, | ||
t, | ||
useDotYouClient, | ||
useSiteData, | ||
} from '@youfoundation/common-app'; | ||
import { useConversations } from '../../hooks/chat/useConversations'; | ||
import { DriveSearchResult } from '@youfoundation/js-lib/core'; | ||
import { Conversation } from '../../providers/ConversationProvider'; | ||
import { useEffect, useState } from 'react'; | ||
import { OdinImage } from '@youfoundation/ui-lib'; | ||
import { HomePageConfig } from '@youfoundation/js-lib/public'; | ||
import { BuiltInProfiles, GetTargetDriveFromProfileId } from '@youfoundation/js-lib/profile'; | ||
|
||
const ConversationsOverview = () => { | ||
return ( | ||
<div className="flex h-screen w-full flex-row overflow-hidden"> | ||
<div className="h-screen w-full max-w-xs flex-shrink-0 border-r bg-page-background p-5 "> | ||
<ProfileHeader /> | ||
<ConversationsList /> | ||
</div> | ||
<div className="h-screen w-full flex-grow bg-background"> | ||
<Chat /> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
const ProfileHeader = () => { | ||
const { data } = useSiteData(); | ||
const { getIdentity, getDotYouClient } = useDotYouClient(); | ||
const dotYouClient = getDotYouClient(); | ||
const odinId = getIdentity() || undefined; | ||
|
||
return ( | ||
<div className="flex flex-row items-center gap-2 pb-5"> | ||
<OdinImage | ||
dotYouClient={dotYouClient} | ||
targetDrive={GetTargetDriveFromProfileId(BuiltInProfiles.StandardProfileId)} | ||
fileId={data?.owner.profileImageFileId} | ||
fileKey={data?.owner.profileImageFileKey} | ||
lastModified={data?.owner.profileImageLastModified} | ||
previewThumbnail={data?.owner.profileImagePreviewThumbnail} | ||
className="aspect-square max-h-[2.5rem] w-full max-w-[2.5rem] rounded-full border border-neutral-200" | ||
fit="cover" | ||
odinId={odinId} | ||
/> | ||
<OwnerName /> | ||
</div> | ||
); | ||
}; | ||
|
||
const ConversationsList = () => { | ||
const [isSearchActive, setIsSearchActive] = useState(false); | ||
const { data: conversations } = useConversations().all; | ||
|
||
const flatConversaions = | ||
(conversations?.pages | ||
?.flatMap((page) => page.searchResults) | ||
?.filter(Boolean) as DriveSearchResult<Conversation>[]) || []; | ||
|
||
return ( | ||
<div className="flex flex-grow flex-col "> | ||
<SearchConversation setIsSearchActive={setIsSearchActive} /> | ||
<div className="flex-grow overflow-auto"> | ||
{flatConversaions?.map((conversation) => ( | ||
<ConversationItem key={conversation.fileId} conversation={conversation} /> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
const ConversationItem = ({ conversation }: { conversation: DriveSearchResult<Conversation> }) => { | ||
return <>{conversation.fileMetadata.appData.content.conversationId}</>; | ||
}; | ||
|
||
const SearchConversation = ({ | ||
setIsSearchActive, | ||
}: { | ||
setIsSearchActive: (isActive: boolean) => void; | ||
}) => { | ||
const [query, setQuery] = useState<string | undefined>(undefined); | ||
|
||
useEffect(() => { | ||
if (query && query.length > 1) setIsSearchActive(true); | ||
else setIsSearchActive(false); | ||
}, [query]); | ||
|
||
return ( | ||
<form onSubmit={(e) => e.preventDefault()}> | ||
<div className="flex flex-row gap-1"> | ||
<Input onChange={(e) => setQuery(e.target.value)} /> | ||
<ActionButton type="secondary">{t('Search')}</ActionButton> | ||
</div> | ||
</form> | ||
); | ||
}; | ||
|
||
const Chat = ({ conversationId }: { conversationId: string }) => { | ||
return ( | ||
<div className="flex h-full flex-grow flex-col"> | ||
<ChatHeader /> | ||
<ChatHistory /> | ||
<ChatComposer /> | ||
</div> | ||
); | ||
}; | ||
|
||
const ChatHeader = () => { | ||
return ( | ||
<div className="flex flex-row items-center gap-2 bg-page-background p-5 "> | ||
<ConnectionImage odinId="sam.dotyou.cloud" className="border border-neutral-200" size="sm" /> | ||
<ConnectionName odinId="sam.dotyou.cloud" /> | ||
</div> | ||
); | ||
}; | ||
|
||
const ChatHistory = () => { | ||
return <div className="h-full flex-grow"></div>; | ||
}; | ||
|
||
const ChatComposer = () => { | ||
return ( | ||
<div className="flex flex-shrink-0 flex-row gap-2 bg-page-background px-5 py-3"> | ||
<div className="my-auto flex flex-row items-center gap-1"> | ||
<EmojiSelector | ||
size="none" | ||
className="px-1 py-1 text-foreground text-opacity-30 hover:text-opacity-100" | ||
onInput={(val) => console.log(val)} | ||
/> | ||
<FileSelector | ||
// onChange={(newFiles) => setAttachment(newFiles?.[0])} | ||
className="text-foreground text-opacity-30 hover:text-opacity-100" | ||
> | ||
<ImageIcon className="h-5 w-5" /> | ||
</FileSelector> | ||
</div> | ||
<VolatileInput placeholder="Your message" className="rounded-md border bg-background p-2" /> | ||
<ActionButton type="secondary">{t('Send')}</ActionButton> | ||
</div> | ||
); | ||
}; | ||
|
||
export default ConversationsOverview; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes
109 changes: 109 additions & 0 deletions
109
packages/owner-app/src/components/Dialog/PushNotificationsDialog/PushNotificationsDialog.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { createPortal } from 'react-dom'; | ||
import { | ||
ErrorNotification, | ||
HardDrive, | ||
SubtleMessage, | ||
Times, | ||
Trash, | ||
t, | ||
} from '@youfoundation/common-app'; | ||
import { usePortal } from '@youfoundation/common-app'; | ||
import { ActionButton } from '@youfoundation/common-app'; | ||
import { DialogWrapper } from '@youfoundation/common-app'; | ||
import { usePushNotificationClients } from '../../../hooks/notifications/usePushNotifications'; | ||
import { PushNotificationSubscription } from '../../../provider/notifications/PushProvider'; | ||
|
||
const PushNotificationsDialog = ({ | ||
isOpen, | ||
onClose, | ||
}: { | ||
isOpen: boolean; | ||
|
||
onClose: () => void; | ||
}) => { | ||
const target = usePortal('modal-container'); | ||
const { data: devices } = usePushNotificationClients().fetch; | ||
const { | ||
removeAll: { | ||
mutate: removeAllDevices, | ||
status: removeAllDevicesStatus, | ||
error: removeAllDevicesError, | ||
}, | ||
} = usePushNotificationClients(); | ||
|
||
if (!isOpen) return null; | ||
const dialog = ( | ||
<DialogWrapper title={t('Push Notifications')} onClose={onClose}> | ||
<ErrorNotification error={removeAllDevicesError} /> | ||
{!devices?.length ? ( | ||
<SubtleMessage>{t('No devices registered')}</SubtleMessage> | ||
) : ( | ||
<> | ||
<div className="grid grid-flow-col gap-4"> | ||
{devices?.map((device) => ( | ||
<DeviceView subscription={device} key={device.accessRegistrationId} /> | ||
))} | ||
</div> | ||
<div className="flex flex-row-reverse"> | ||
<ActionButton | ||
type="remove" | ||
icon={Trash} | ||
onClick={() => removeAllDevices()} | ||
state={removeAllDevicesStatus} | ||
> | ||
{t('Remove all')} | ||
</ActionButton> | ||
</div> | ||
</> | ||
)} | ||
</DialogWrapper> | ||
); | ||
|
||
return createPortal(dialog, target); | ||
}; | ||
|
||
const DeviceView = ({ | ||
subscription, | ||
className, | ||
}: { | ||
subscription: PushNotificationSubscription; | ||
className?: string; | ||
}) => { | ||
const { | ||
fetchCurrent: { data: currentDevice }, | ||
removeCurrent: { | ||
mutate: removeCurrentDevice, | ||
status: removeCurrentDeviceStatus, | ||
error: removeCurrentDeviceError, | ||
}, | ||
} = usePushNotificationClients(); | ||
|
||
return ( | ||
<> | ||
<ErrorNotification error={removeCurrentDeviceError} /> | ||
<div className={`flex flex-row items-center ${className ?? ''}`}> | ||
<HardDrive className="mb-auto mr-3 mt-1 h-6 w-6" /> | ||
<div className="mr-2 flex flex-col"> | ||
{subscription.friendlyName} | ||
<small className="block text-sm"> | ||
{t('Created')}: {new Date(subscription.subscriptionStartedDate).toLocaleDateString()} | ||
</small> | ||
</div> | ||
{currentDevice?.accessRegistrationId === subscription.accessRegistrationId ? ( | ||
<ActionButton | ||
icon={Times} | ||
type="secondary" | ||
size="square" | ||
className="ml-2" | ||
onClick={async () => { | ||
removeCurrentDevice(); | ||
}} | ||
state={removeCurrentDeviceStatus} | ||
/> | ||
) : null} | ||
</div> | ||
</> | ||
); | ||
}; | ||
|
||
export default PushNotificationsDialog; |
87 changes: 87 additions & 0 deletions
87
packages/owner-app/src/hooks/notifications/usePushNotifications.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { useDotYouClient } from '@youfoundation/common-app'; | ||
import { | ||
GetApplicationServerKey, | ||
GetCurrentDeviceDetails, | ||
GetRegisteredDevices, | ||
RegisterNewDevice, | ||
RemoveAllRegisteredDevice, | ||
RemoveRegisteredDevice, | ||
} from '../../provider/notifications/PushProvider'; | ||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | ||
|
||
export const usePushNotifications = () => { | ||
const dotYouClient = useDotYouClient().getDotYouClient(); | ||
|
||
// Register the push Application Server | ||
// Use serviceWorker.ready to ensure that you can subscribe for push | ||
return { | ||
isEnabled: Notification.permission === 'granted', | ||
enableOnThisDevice: async () => { | ||
await Notification.requestPermission(); | ||
console.log('Notification permission granted.'); | ||
await navigator.serviceWorker.ready.then(async (serviceWorkerRegistration) => { | ||
const publicKey = await GetApplicationServerKey(); | ||
console.log(publicKey); | ||
const options = { | ||
userVisibleOnly: true, | ||
applicationServerKey: publicKey, | ||
}; | ||
|
||
serviceWorkerRegistration.pushManager.subscribe(options).then( | ||
async (pushSubscription) => { | ||
await RegisterNewDevice(dotYouClient, pushSubscription); | ||
alert('successfully registered'); | ||
}, | ||
(error) => { | ||
console.error(error); | ||
} | ||
); | ||
}); | ||
}, | ||
}; | ||
}; | ||
|
||
export const usePushNotificationClients = () => { | ||
const queryClient = useQueryClient(); | ||
const dotYouClient = useDotYouClient().getDotYouClient(); | ||
|
||
const getCurrentClient = async () => { | ||
return await GetCurrentDeviceDetails(dotYouClient); | ||
}; | ||
|
||
const removeCurrentDevice = async () => { | ||
return await RemoveRegisteredDevice(dotYouClient); | ||
}; | ||
|
||
const getNotificationClients = async () => { | ||
return await GetRegisteredDevices(dotYouClient); | ||
}; | ||
|
||
const removeAllClients = async () => { | ||
return await RemoveAllRegisteredDevice(dotYouClient); | ||
}; | ||
|
||
return { | ||
fetch: useQuery({ | ||
queryKey: ['notification-clients'], | ||
queryFn: () => getNotificationClients(), | ||
}), | ||
removeAll: useMutation({ | ||
mutationFn: () => removeAllClients(), | ||
onSettled: () => { | ||
queryClient.invalidateQueries({ queryKey: ['notification-clients'] }); | ||
}, | ||
}), | ||
|
||
fetchCurrent: useQuery({ | ||
queryKey: ['notification-clients', 'current'], | ||
queryFn: () => getCurrentClient(), | ||
}), | ||
removeCurrent: useMutation({ | ||
mutationFn: () => removeCurrentDevice(), | ||
onSettled: () => { | ||
queryClient.invalidateQueries({ queryKey: ['notification-clients'], exact: false }); | ||
}, | ||
}), | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.