react-device-detect#isSafari JavaScript Examples
The following examples show how to use
react-device-detect#isSafari.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: InfiniteScroller.js From website with MIT License | 6 votes |
componentDidUpdate() {
if (this.props.isReverse && this.loadMore) {
const parentElement = this.getParentElement(this.scrollComponent);
// after loading more, safari's scrollTop need not to be adjusted as it is not
// affected (since it is a negative value w.r.t the bottom instead of the top)
if (!isSafari) {
parentElement.scrollTop = parentElement.scrollHeight - this.beforeScrollHeight + this.beforeScrollTop;
}
this.loadMore = false;
}
this.attachScrollListener();
}
Example #2
Source File: CardLog.jsx From ashteki with GNU Affero General Public License v3.0 | 5 votes |
CardLog = ({ cards, onMouseOut, onMouseOver }) => {
const [show, setShow] = useState(true);
if (!cards) {
return null;
}
const renderSimpleCard = (card) => {
if (!show) return '';
if (card.type === 'die') {
return (
<div className='x-large cardlog-die mb-2'>
<DieIcon key={'cld-' + card.uuid} die={card} />
</div>
)
}
if (!card.id) return '';
return (
<div
className='target-card vertical mb-2'
onMouseOut={() => onMouseOut && onMouseOut(card)}
onMouseOver={() => onMouseOver && onMouseOver(card)}
>
<CardImage card={card} />
</div>
);
};
const renderLastCard = (card) => {
if (card.type === 'die') {
return (
<div className='x-large cardlog-die mb-2'>
<DieIcon key={'cld-' + card.uuid} die={card} />
</div>
)
}
if (!card.id) return '';
return (
<div
className='last-card vertical mb-2'
onMouseOut={() => onMouseOut && onMouseOut(card)}
onMouseOver={() => onMouseOver && onMouseOver(card)}
>
<CardImage card={card} />
</div>
);
};
let logLength = cards.length - 1;
if (isSafari) {
logLength = Math.min(3, logLength);
}
const cardPics =
cards.length > 1 ? cards.slice(0, logLength).map((c) => renderSimpleCard(c)) : null;
const firstCard = cards.length ? renderLastCard(cards[logLength]) : null;
// const size = card.type === 'decklist' ? 'x-large' : 'normal';
const arrow = show ? '︿' : '﹀';
return (
<div className='cardlog-wrapper'>
{firstCard}
<div className='card-log bg-dark'>
{cardPics}
<div className='card-log-arrow' onClick={() => setShow(!show)}>
{arrow}
</div>
</div>
</div>
);
}
Example #3
Source File: InfiniteScroller.js From website with MIT License | 5 votes |
scrollListener() {
const el = this.scrollComponent;
const scrollEl = window;
const parentNode = this.getParentElement(el);
let offset;
if (this.props.useWindow) {
const doc = document.documentElement || document.body.parentNode || document.body;
const scrollTop = scrollEl.pageYOffset !== undefined ? scrollEl.pageYOffset : doc.scrollTop;
if (this.props.isReverse) {
offset = scrollTop;
} else {
offset = this.calculateOffset(el, scrollTop);
}
} else if (this.props.isReverse) {
if (isSafari) {
// known issue with safari:
// scrollTop will be starting from 0 (instead of scrollHeight) even when `flex-direction: column-reverse`,
// hence the scroll top will be a negative value when scrolling from the div, need to handle separately
// reference: https://bugzilla.mozilla.org/show_bug.cgi?id=1042151#c36
offset = parentNode.scrollHeight + parentNode.scrollTop - parentNode.clientHeight;
} else {
offset = parentNode.scrollTop;
}
} else {
offset = el.scrollHeight - parentNode.scrollTop - parentNode.clientHeight;
}
// Here we make sure the element is visible as well as checking the offset
if (offset < Number(this.props.threshold) && el && el.offsetParent !== null) {
this.detachScrollListener();
this.beforeScrollHeight = parentNode.scrollHeight;
this.beforeScrollTop = parentNode.scrollTop;
// Call loadMore after detachScrollListener to allow for non-async loadMore functions
// added check for this.props.hasMore to prevent loading more when it does not have more
if (typeof this.props.loadMore === 'function' && this.props.hasMore) {
this.props.loadMore((this.pageLoaded += 1));
this.loadMore = true;
}
}
}
Example #4
Source File: index.js From website with MIT License | 4 votes |
ChatDialogMessages = ({ postType, inputRowHeight, isShowPostDetails }) => {
const { state } = useContext(ChatContext);
const loggedInUser = getUser(state);
const isNewChat = getIsNewChat(state);
const selectedChatId = getSelectedChatId(state);
const navBarHeight = useNavbarHeight();
const [chatMessageDocs, setChatMessageDocs] = useState([]);
// only consider loading more when it's not a new chat, since it's impossible to have a new chat
// to have messages initially
const [shouldSeeMore, setShouldSeeMore] = useState(!isNewChat);
const [isLoadingMore, setIsLoadingMore] = useState(false); // to prevent multiple loads at the same time
const [isShowScrollerButton, setIsShowScrollerButton] = useState(false);
const [unreadCount, setUnreadCount] = useState(0);
const { isTablet } = useMediaQuery();
const bottomOfScrollerRef = useRef(null);
const scrollerRef = useRef(null);
const { height: viewportHeight } = useWindowDimensions();
useEffect(() => {
// reset values every time selectedChatId changes
setChatMessageDocs([]);
setShouldSeeMore(!isNewChat);
setIsShowScrollerButton(false);
let unsubscribeFunction;
const { userId } = loggedInUser.user;
// when selected a chat, subscribe to the corresponding chat messages
if (selectedChatId !== null || !isNewChat) {
api.chats
.subscribeToChatMessages(selectedChatId, userId, updateChatMessages)
.then((fn) => (unsubscribeFunction = fn))
.then(() => {
api.chats.activateUserChatPresence(selectedChatId, userId);
});
disableFurtherLoadsIfMessagesLessThanOneBatch();
}
return () => {
if (unsubscribeFunction) {
api.chats.unsubscribeFromChatMessages(selectedChatId, userId, unsubscribeFunction).then(() => {
api.chats.deactivateUserChatPresence(selectedChatId, userId);
setChatMessageDocs([]);
});
}
};
}, [selectedChatId]);
const scrollToBottomIfSentByLoggedInUser = (chatMessage) => {
if (chatMessage.sender.id === loggedInUser.user.userId) {
bottomOfScrollerRef.current.scrollIntoView();
}
};
const incrementUnreadCountIfSentByOppositeUser = (chatMessage) => {
if (chatMessage.sender.id !== loggedInUser.user.userId) {
const bottomScrollTop = isSafari ? 0 : scrollerRef.current.scrollHeight - scrollerRef.current.clientHeight;
if (scrollerRef.current.scrollTop < bottomScrollTop) {
setUnreadCount((unreadCount) => unreadCount + 1);
}
}
};
/**
* Callback function that is called when:
* i) fetching the initial batch of chat messages
* ii) when new chat messages are received
*/
const updateChatMessages = (chatMessageDoc) => {
let isNewlySentMessage = false;
setChatMessageDocs((prevChatMessageDocs) => {
const newChatMessage = chatMessageDoc.data();
const lastChatMessageDoc = prevChatMessageDocs[prevChatMessageDocs.length - 1];
const firstChatMessageDoc = prevChatMessageDocs[0];
// insert chat message doc to the front if no messages or the message is an older message
if (
!firstChatMessageDoc ||
firstChatMessageDoc.data().dateTime.toMillis() >= newChatMessage.dateTime.toMillis()
) {
return [chatMessageDoc, ...prevChatMessageDocs];
}
if (lastChatMessageDoc.data().dateTime.toMillis() <= newChatMessage.dateTime.toMillis()) {
// insert chat message doc to the back if it is a newly sent message
isNewlySentMessage = true;
return [...prevChatMessageDocs, chatMessageDoc];
} else {
// insert chat message doc to the correct position if not newly sent message
// note: this occurs when there are concurrency issues when sending and receiving messages
for (let i = prevChatMessageDocs.length - 1; i >= 0; i--) {
const currMessage = prevChatMessageDocs[i].data();
if (newChatMessage.dateTime.toMillis() > currMessage.dateTime.toMillis()) {
return [...prevChatMessageDocs.slice(0, i + 1), chatMessageDoc, ...prevChatMessageDocs.slice(i + 1)];
}
}
}
});
if (isNewlySentMessage) {
scrollToBottomIfSentByLoggedInUser(chatMessageDoc.data());
incrementUnreadCountIfSentByOppositeUser(chatMessageDoc.data());
}
};
/**
* A hacky way to determine if we should load more after the first batch,
* since the subscription method does not tell whether the first load of chat
* messages are less than one CHAT_MESSAGES_BATCH_SIZE
*/
const disableFurtherLoadsIfMessagesLessThanOneBatch = () => {
api.chats.getChatMessages(selectedChatId).then((rawNewChatMessages) => {
if (rawNewChatMessages.docs.length < CHAT_MESSAGES_BATCH_SIZE) {
// loaded all chat messages
setShouldSeeMore(false);
}
});
};
const handleOnSeeMore = () => {
if (chatMessageDocs.length === 0) {
return; // assuming that you can see more after loading the initial batch
}
setIsLoadingMore(true);
api.chats.getChatMessages(selectedChatId, chatMessageDocs[0]).then((rawNewChatMessages) => {
// need to reverse the new chat message docs since the order of the array is latest -> oldest, but in our
// array the order is oldest -> latest
const newChatMessageDocs = rawNewChatMessages.docs.reverse();
setChatMessageDocs([...newChatMessageDocs, ...chatMessageDocs]);
if (newChatMessageDocs.length < CHAT_MESSAGES_BATCH_SIZE) {
// loaded all chat messages
setShouldSeeMore(false);
}
setIsLoadingMore(false);
});
};
/**
* Handler to show the scroller button if scroll position is not at the bottom
*/
const scrollHandler = () => {
// note:
// safari and chrome browsers' bottom has scrollTop of 0, negative values when scroll up
// other browsers' bottom has scrollTop of scrollHeight - clientHeight
const bottomScrollTop = isSafari ? 0 : scrollerRef.current.scrollHeight - scrollerRef.current.clientHeight;
const currScrollTop = scrollerRef.current.scrollTop;
if (currScrollTop < bottomScrollTop && currScrollTop !== 0) {
setIsShowScrollerButton(true);
} else {
setIsShowScrollerButton(false);
setUnreadCount(0);
}
};
// get all heights of components within the chatDialog, only inputRowHeight is passed in as
// the height is not constant
const { chatDialogBackButton, chatDialogSeePostRow, chatDialogUserRow, chatDialogMessagesPadding } = isTablet
? desktopHeights
: mobileHeights;
let sumOfOtherComponentHeights = chatDialogBackButton + chatDialogMessagesPadding + inputRowHeight;
if (isShowPostDetails) {
// only add the heights of see post row and user row if it is going to be shown
sumOfOtherComponentHeights += chatDialogSeePostRow + chatDialogUserRow;
}
const offsetHeight = navBarHeight + sumOfOtherComponentHeights;
// display tips before first message is being sent for a wish
if (isNewChat && !selectedChatId && postType === wishes) {
return (
<CardSection>
<MessageContainer offsetHeight={offsetHeight} viewportHeight={viewportHeight}>
<NewChatTipsForWish postType={postType} />
</MessageContainer>
</CardSection>
);
}
return (
<CardSection>
<MessageContainer
offsetHeight={offsetHeight}
viewportHeight={viewportHeight}
onScroll={scrollHandler}
ref={scrollerRef}
>
<InfiniteScroll
loadMore={handleOnSeeMore}
hasMore={shouldSeeMore && !isLoadingMore}
isReverse
initialLoad={false}
useWindow={false}
loader={<Loading type="pageLoader" key={0} />}
>
<Stack direction="column">
{chatMessageDocs &&
chatMessageDocs.map((messageDoc) => {
const message = messageDoc.data();
const { sender, content, dateTime } = message;
// right side is logged in user's messages, left side is opposite user's
return sender.id === loggedInUser.user.userId ? (
<RightMessageSection
key={`${messageDoc.id}`}
message={message}
loggedInUser={loggedInUser}
selectedChatId={selectedChatId}
offsetHeight={offsetHeight}
/>
) : (
<LeftMessageSection
key={`${messageDoc.id}`}
message={message}
loggedInUser={loggedInUser}
selectedChatId={selectedChatId}
offsetHeight={offsetHeight}
/>
);
})}
</Stack>
<div ref={bottomOfScrollerRef} />
</InfiniteScroll>
</MessageContainer>
{isShowScrollerButton && (
<ScrollToBottomContainer>
<ScrollerButtonContainer>
<Button
circled
transparent
iconLeft={<ChevronDown />}
asComponent={ScrollToBottomButton}
onClick={() => bottomOfScrollerRef.current.scrollIntoView({ behavior: 'smooth' })}
></Button>
</ScrollerButtonContainer>
{unreadCount > 0 && (
<NotificationBadgeWrapper>
<NotificationBadge type="info">{unreadCount}</NotificationBadge>
</NotificationBadgeWrapper>
)}
</ScrollToBottomContainer>
)}
</CardSection>
);
}
Example #5
Source File: WyreTopUpBalanceDropdown.jsx From v3-ui with MIT License | 4 votes |
export function WyreTopUpBalanceDropdown(props) {
const { t } = useTranslation()
const { label, className, hoverTextColor, textColor, tickerUpcased } = props
const onValueSet = (currency) => {
handleOpenWyre(currency)
}
const applePay = (
<>
{isSafari && (
<>
, <img src={ApplePay} className='inline-block relative h-6 w-12' style={{ top: 0 }} />
</>
)}
</>
)
const currencies = {
[tickerUpcased]: {
label: (
<span className='text-xs'>
{t('buyTickerDebitCreditCard', {
ticker: tickerUpcased
})}
{applePay}
</span>
)
},
ETH: {
label: (
<span className='text-xs'>
{t('buyEthDebitCreditCard')}
{applePay}
</span>
)
}
}
const handleOpenWyre = async (currency) => {
const { usersAddress } = props
const params = {
path: `/v3/orders/reserve`,
dest: `ethereum:${usersAddress}`,
destCurrency: currency.toUpperCase()
}
let response
try {
response = await axiosInstance.post(`${WYRE_LAMBDA_PATH}`, params)
// dropdownRef.handleClose()
const url = response?.data?.url
if (url) {
window.open(url)
} else {
console.warn(response.error)
}
} catch (e) {
poolToast.error(`Wyre - purchase error, please try again or message support`)
console.error(e)
}
}
const formatValue = (key) => {
const currency = currencies[key]
return <>{currency.label}</>
}
return (
<>
<span className='relative z-50'>
<DropdownList
id='topup-dropdown'
label={label}
className={className}
textColor={textColor}
hoverTextColor={hoverTextColor}
formatValue={formatValue}
onValueSet={onValueSet}
current={null}
values={currencies}
/>
</span>
</>
)
}