mirror of
				https://github.com/cinnyapp/cinny.git
				synced 2025-11-04 14:30:29 +03:00 
			
		
		
		
	Show pin/unpin events in the timeline
This commit is contained in:
		
							parent
							
								
									7f40605bfe
								
							
						
					
					
						commit
						4180e79794
					
				
					 5 changed files with 125 additions and 0 deletions
				
			
		
							
								
								
									
										8
									
								
								src/app/components/message/MessageJumpLink.css.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/app/components/message/MessageJumpLink.css.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
import { style } from '@vanilla-extract/css';
 | 
			
		||||
 | 
			
		||||
export const messageJumpLink = style({
 | 
			
		||||
  ':hover': {
 | 
			
		||||
    cursor: 'pointer',
 | 
			
		||||
    textDecoration: 'underline',
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										13
									
								
								src/app/components/message/MessageJumpLink.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/app/components/message/MessageJumpLink.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
import React from "react";
 | 
			
		||||
import { messageJumpLink } from "./MessageJumpLink.css";
 | 
			
		||||
import { useRoomNavigate } from "../../hooks/useRoomNavigate";
 | 
			
		||||
 | 
			
		||||
export function MessageJumpLink({ roomId, eventId }: { roomId: string, eventId: string }) {
 | 
			
		||||
  const { navigateRoom } = useRoomNavigate();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <button type="button" className={messageJumpLink} onClick={() => navigateRoom(roomId, eventId)}>
 | 
			
		||||
      a message
 | 
			
		||||
    </button>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -126,6 +126,7 @@ import { useAccessiblePowerTagColors, useGetMemberPowerTag } from '../../hooks/u
 | 
			
		|||
import { useTheme } from '../../hooks/useTheme';
 | 
			
		||||
import { useRoomCreatorsTag } from '../../hooks/useRoomCreatorsTag';
 | 
			
		||||
import { usePowerLevelTags } from '../../hooks/usePowerLevelTags';
 | 
			
		||||
import { usePinnedEventParser } from '../../hooks/usePinnedEventParser';
 | 
			
		||||
 | 
			
		||||
const TimelineFloat = as<'div', css.TimelineFloatVariants>(
 | 
			
		||||
  ({ position, className, ...props }, ref) => (
 | 
			
		||||
| 
						 | 
				
			
			@ -532,6 +533,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
 | 
			
		|||
    [mx, room, linkifyOpts, spoilerClickHandler, mentionClickHandler, useAuthentication]
 | 
			
		||||
  );
 | 
			
		||||
  const parseMemberEvent = useMemberEventParser();
 | 
			
		||||
  const parsePinnedEvent = usePinnedEventParser(room.roomId);
 | 
			
		||||
 | 
			
		||||
  const [timeline, setTimeline] = useState<Timeline>(() =>
 | 
			
		||||
    eventId ? getEmptyTimeline() : getInitialTimeline(room)
 | 
			
		||||
| 
						 | 
				
			
			@ -1468,6 +1470,47 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
 | 
			
		|||
          </Event>
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
      [StateEvent.RoomPinnedEvents]: (mEventId, mEvent, item) => {
 | 
			
		||||
        const highlighted = focusItem?.index === item && focusItem.highlight;
 | 
			
		||||
        const parsed = parsePinnedEvent(mEvent);
 | 
			
		||||
 | 
			
		||||
        const timeJSX = (
 | 
			
		||||
          <Time
 | 
			
		||||
            ts={mEvent.getTs()}
 | 
			
		||||
            compact={messageLayout === MessageLayout.Compact}
 | 
			
		||||
            hour24Clock={hour24Clock}
 | 
			
		||||
            dateFormatString={dateFormatString}
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
          <Event
 | 
			
		||||
            key={mEvent.getId()}
 | 
			
		||||
            data-message-item={item}
 | 
			
		||||
            data-message-id={mEventId}
 | 
			
		||||
            room={room}
 | 
			
		||||
            mEvent={mEvent}
 | 
			
		||||
            highlight={highlighted}
 | 
			
		||||
            messageSpacing={messageSpacing}
 | 
			
		||||
            canDelete={canRedact || mEvent.getSender() === mx.getUserId()}
 | 
			
		||||
            hideReadReceipts={hideActivity}
 | 
			
		||||
            showDeveloperTools={showDeveloperTools}
 | 
			
		||||
          >
 | 
			
		||||
            <EventContent
 | 
			
		||||
              messageLayout={messageLayout}
 | 
			
		||||
              time={timeJSX}
 | 
			
		||||
              iconSrc={Icons.Pin}
 | 
			
		||||
              content={
 | 
			
		||||
                <Box grow="Yes" direction="Column">
 | 
			
		||||
                  <Text size="T300" priority="300">
 | 
			
		||||
                    {parsed}
 | 
			
		||||
                  </Text>
 | 
			
		||||
                </Box>
 | 
			
		||||
              }
 | 
			
		||||
            />
 | 
			
		||||
          </Event>
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    (mEventId, mEvent, item) => {
 | 
			
		||||
      if (!showHiddenEvents) return null;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										57
									
								
								src/app/hooks/usePinnedEventParser.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/app/hooks/usePinnedEventParser.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
import { MatrixEvent } from 'matrix-js-sdk';
 | 
			
		||||
import React, { ReactNode } from 'react';
 | 
			
		||||
import { IRoomPinnedEventsContent } from '../../types/matrix/room';
 | 
			
		||||
import { getMxIdLocalPart } from '../utils/matrix';
 | 
			
		||||
import { MessageJumpLink } from '../components/message/MessageJumpLink';
 | 
			
		||||
 | 
			
		||||
export type PinnedEventParser = (mEvent: MatrixEvent) => ReactNode;
 | 
			
		||||
 | 
			
		||||
export const usePinnedEventParser = (roomId: string): PinnedEventParser => {
 | 
			
		||||
  const pinnedEventParser = (mEvent: MatrixEvent) => {
 | 
			
		||||
    const { pinned } = mEvent.getContent<IRoomPinnedEventsContent>();
 | 
			
		||||
    const prevPinned = (mEvent.getPrevContent() as Partial<IRoomPinnedEventsContent>).pinned;
 | 
			
		||||
    const senderId = mEvent.getSender() ?? '';
 | 
			
		||||
    const senderName = getMxIdLocalPart(senderId);
 | 
			
		||||
 | 
			
		||||
    const addedPins = pinned.filter((pdu) => !(prevPinned?.includes(pdu) ?? false));
 | 
			
		||||
    const removedPins = prevPinned?.filter((pdu) => !pinned.includes(pdu)) ?? [];
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <>
 | 
			
		||||
        <b>{senderName}</b>
 | 
			
		||||
        {addedPins.length === 0 && removedPins.length === 0 ? (
 | 
			
		||||
          ' made no changes to the pinned messages'
 | 
			
		||||
        ) : (
 | 
			
		||||
          <>
 | 
			
		||||
            {addedPins.length > 0 && (
 | 
			
		||||
              <>
 | 
			
		||||
                {' pinned '}
 | 
			
		||||
                <b>
 | 
			
		||||
                  {addedPins.length === 1 ? (
 | 
			
		||||
                    <MessageJumpLink roomId={roomId} eventId={addedPins[0]} />
 | 
			
		||||
                  ) : (
 | 
			
		||||
                    `${addedPins.length} messages`
 | 
			
		||||
                  )}
 | 
			
		||||
                </b>
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
            {addedPins.length > 0 && removedPins.length > 0 && 'and'}
 | 
			
		||||
            {removedPins.length > 0 && (
 | 
			
		||||
              <>
 | 
			
		||||
                {' unpinned '}
 | 
			
		||||
                <b>
 | 
			
		||||
                  {removedPins.length === 1 ? (
 | 
			
		||||
                    <MessageJumpLink roomId={roomId} eventId={removedPins[0]} />
 | 
			
		||||
                  ) : (
 | 
			
		||||
                    `${removedPins.length} messages`
 | 
			
		||||
                  )}
 | 
			
		||||
                </b>
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
          </>
 | 
			
		||||
        )}
 | 
			
		||||
      </>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
  return pinnedEventParser;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -77,6 +77,10 @@ export type IRoomCreateContent = {
 | 
			
		|||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type IRoomPinnedEventsContent = {
 | 
			
		||||
  pinned: string[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type GetContentCallback = <T>() => T;
 | 
			
		||||
 | 
			
		||||
export type RoomToParents = Map<string, Set<string>>;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue