Skip to content

Commit bc3cddb

Browse files
authored
Merge pull request #16 from mstephen19/develop
Develop Branch
2 parents d4100b5 + aa9aee8 commit bc3cddb

File tree

17 files changed

+110
-66
lines changed

17 files changed

+110
-66
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ Tab Samurai offers four primary features.
4646

4747
## License
4848

49-
Tab Samurai is an open-source project operating under a modified MIT license. [See license](./LICENSE).
49+
Tab Samurai is an open-source project operating under a modified MIT license. [See license](./LICENSE)

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": 3,
33
"name": "Tab Samurai",
44
"short_name": "Tabs",
5-
"version": "1.2.0",
5+
"version": "1.2.1",
66
"description": "Take your browsing experience to the next level with automatic tab hibernation, smart tab management, and intuitive tab recovery.",
77
"icons": {
88
"16": "public/icon16.png",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "tabs",
33
"private": true,
4-
"version": "1.2.0",
4+
"version": "1.2.1",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

site/src/app/layout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ export const metadata: Metadata = {
4949
description:
5050
'Take your browsing experience to the next level with automatic tab hibernation, smart tab management, and intuitive tab recovery.',
5151
},
52+
other: {
53+
'google-site-verification': 'Bd4LAkjfHg1A--AkO0gk0s6_-pa5vmpnM2m4CoXkJIM',
54+
},
5255
};
5356

5457
export default function RootLayout({

src/background/updates.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export const initialize = async () => {
1111

1212
// If an update is available & not already tracked
1313
if (updateAvailable && updateDetails.version !== availableUpdateVersion) {
14-
console.log('hi');
1514
store.availableUpdateVersion.write(updateDetails.version);
1615
}
1716

src/consts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const HELP_AND_INFO_FAQ: { primary: string; secondary: string }[] = [
3636
{
3737
primary: 'How do I customize which tabs should hibernate?',
3838
secondary:
39-
'Tab Samurai will auto-hibernate a tab once it hasn\'t been viewed for the duration specified within "Settings". You can add domains (e.g. "https://google.com") to the whitelist to ensure they don\'t hibernate. By default, pinned tabs don\'t hibernate; however this option can be switched to "Yes". Your active tab (the one you\'re looking at) will never hibernate. Tabs playing audio will also never hibernate.',
39+
'Tab Samurai will auto-hibernate a tab once it hasn\'t been viewed for the duration specified within "Hibernation Settings". You can add domains (e.g. "https://google.com") to the whitelist to ensure they don\'t hibernate. By default, pinned tabs don\'t hibernate; however this option can be switched to "Yes". Your active tab (the one you\'re looking at) will never hibernate. Tabs playing audio or recording via your webcam/microphone will never hibernate.',
4040
},
4141
{
4242
primary: "Will I lose my tab's data when it hibernates?",

src/popup/Accordions/Info/index.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { Box, Divider, List, ListItem, ListItemText } from '@mui/material';
2-
import { FEATURE_REQUEST_URL, HELP_AND_INFO_FAQ, REPORT_BUG_URL } from '../../../consts';
2+
import { FEATURE_REQUEST_URL, HELP_AND_INFO_FAQ, REPORT_BUG_URL, REVIEW_EXTENSION_URL } from '../../../consts';
33
import { Link } from '../../components/Link';
4+
import TipsAndUpdatesIcon from '@mui/icons-material/TipsAndUpdates';
5+
import BugReportIcon from '@mui/icons-material/BugReport';
6+
import StarIcon from '@mui/icons-material/Star';
47

58
export const Info = () => {
69
return (
@@ -15,10 +18,24 @@ export const Info = () => {
1518

1619
<Divider flexItem />
1720

18-
<Box display='flex' gap='10px'>
19-
<Link url={FEATURE_REQUEST_URL}>Request a Feature</Link>
21+
<Box display='flex' justifyContent='space-between'>
22+
<Box component='span' display='flex' alignItems='center' gap='5px'>
23+
<TipsAndUpdatesIcon sx={{ fontSize: '1rem' }} />
2024

21-
<Link url={REPORT_BUG_URL}>Report a Bug</Link>
25+
<Link url={FEATURE_REQUEST_URL}>Request a Feature</Link>
26+
</Box>
27+
28+
<Box component='span' display='flex' alignItems='center' gap='5px'>
29+
<BugReportIcon sx={{ fontSize: '1rem' }} />
30+
31+
<Link url={REPORT_BUG_URL}>Report a Bug</Link>
32+
</Box>
33+
34+
<Box component='span' display='flex' alignItems='center' gap='5px'>
35+
<StarIcon sx={{ fontSize: '1rem' }} />
36+
37+
<Link url={REVIEW_EXTENSION_URL}>Leave a Review</Link>
38+
</Box>
2239
</Box>
2340
</Box>
2441
);

src/popup/Accordions/QuickActions/index.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ConfigContext } from '../../context/ConfigProvider';
55
import { pluralize, tabs as tabUtils } from '../../../utils';
66
import { toast } from '../../Toast';
77
import { PageStateContext } from '../../context/PageStateProvider';
8+
import { OPENED_IN_POPOUT } from '../../consts';
89

910
const QuickActionButton = styled((props: ButtonProps) => <Button {...props} variant='contained' />)({
1011
textTransform: 'none',
@@ -20,10 +21,10 @@ export const QuickActions = () => {
2021
(tab) => tab.discarded || tab.active || tabUtils.shouldWhitelist(tab, config, pageStates)
2122
);
2223

24+
const inactiveTabs = tabs.filter((tab) => !tab.active && !tab.discarded && !tabUtils.shouldWhitelist(tab, config, pageStates));
25+
2326
const handleSuspendInactiveTabs = useCallback(async () => {
2427
try {
25-
const inactiveTabs = tabs.filter((tab) => !tab.active && !tab.discarded && !tabUtils.shouldWhitelist(tab, config, pageStates));
26-
2728
await Promise.all(inactiveTabs.map((tab) => chrome.tabs.discard(tab.id!)));
2829

2930
toast({
@@ -36,9 +37,9 @@ export const QuickActions = () => {
3637
message: 'Failed to hibernate inactive tabs.',
3738
});
3839
}
39-
}, [tabs, config, pageStates]);
40+
}, [inactiveTabs]);
4041

41-
const audibleTabs = tabs.filter((tab) => tab.audible && !tab.mutedInfo?.muted);
42+
const audibleTabs = useMemo(() => tabs.filter((tab) => tab.audible && !tab.mutedInfo?.muted), [tabs]);
4243
const someTabsAudible = Boolean(audibleTabs.length);
4344

4445
const handleMuteMediaTabs = useCallback(async () => {
@@ -57,7 +58,8 @@ export const QuickActions = () => {
5758
}
5859
}, [audibleTabs]);
5960

60-
const unpinnedTabs = tabs.filter((tab) => !tab.pinned && !tab.active);
61+
// Allow closing ALL unpinned tabs, even the active one, only if opened in popout mode
62+
const unpinnedTabs = useMemo(() => tabs.filter((tab) => !tab.pinned && (OPENED_IN_POPOUT || !tab.active)), [tabs]);
6163
const someTabsUnpinned = Boolean(unpinnedTabs.length);
6264

6365
const handleCloseUnpinnedTabs = useCallback(async () => {
@@ -99,22 +101,22 @@ export const QuickActions = () => {
99101
<Box display='flex' gap='5px' flexWrap='wrap'>
100102
<Tooltip title={`Found ${duplicateTabs.length} tabs which are duplicates`}>
101103
<QuickActionButton disabled={!someDuplicateTabs} onClick={handleCloseDuplicateTabs}>
102-
Close Duplicate Tabs
104+
Close Duplicate Tabs {Boolean(duplicateTabs.length) && `(${duplicateTabs.length})`}
103105
</QuickActionButton>
104106
</Tooltip>
105107

106108
<QuickActionButton disabled={allTabsDiscardedActiveOrWhitelisted} onClick={handleSuspendInactiveTabs}>
107-
Hibernate All Inactive Tabs
109+
Hibernate All Inactive Tabs {Boolean(inactiveTabs.length) && `(${inactiveTabs.length})`}
108110
</QuickActionButton>
109111

110112
<Tooltip title={`Found ${audibleTabs.length} audible tabs`}>
111113
<QuickActionButton disabled={!someTabsAudible} onClick={handleMuteMediaTabs}>
112-
Mute All Media-Playing Tabs
114+
Mute All Media-Playing Tabs {Boolean(audibleTabs.length) && `(${audibleTabs.length})`}
113115
</QuickActionButton>
114116
</Tooltip>
115117

116118
<QuickActionButton disabled={!someTabsUnpinned} onClick={handleCloseUnpinnedTabs}>
117-
Close All Unpinned Tabs
119+
Close All Unpinned Tabs {Boolean(unpinnedTabs.length) && `(${unpinnedTabs.length})`}
118120
</QuickActionButton>
119121
</Box>
120122
);

src/popup/Accordions/TabManager/SearchTabsBox.tsx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,17 @@ export const SearchTabsBox = memo(
6060
/>
6161
</Paper>
6262

63-
<Paper
64-
elevation={3}
65-
sx={{
66-
display: 'flex',
67-
placeItems: 'center',
68-
border: '1px solid white',
69-
}}>
70-
<Tooltip arrow title='Group By' placement='top'>
63+
<Tooltip arrow title='Group By' placement='top'>
64+
<Paper
65+
onClick={handleOpenMenu}
66+
elevation={3}
67+
sx={{
68+
cursor: 'pointer',
69+
display: 'flex',
70+
placeItems: 'center',
71+
border: '1px solid white',
72+
}}>
7173
<IconButton
72-
onClick={handleOpenMenu}
7374
disableRipple
7475
size='small'
7576
sx={{
@@ -79,8 +80,8 @@ export const SearchTabsBox = memo(
7980
}}>
8081
<CompressIcon />
8182
</IconButton>
82-
</Tooltip>
83-
</Paper>
83+
</Paper>
84+
</Tooltip>
8485

8586
<HoverMenu anchorEl={anchor} open={open} onClose={handleCloseMenu}>
8687
{MANAGE_TABS_GROUP_OPTIONS.map((option) => (

src/popup/Accordions/TabManager/TabManagerListItem.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -286,16 +286,23 @@ const TabGroupListItem = ({
286286
const handleCloseTabs = useCallback(async () => {
287287
try {
288288
const lastFocusedWindow = await chrome.windows.getLastFocused();
289+
const tabGroupWindowId = groupType === 'Window' ? tabs.find(({ windowId }) => windowId)?.windowId : null;
290+
291+
// Close whole window if grouping by window, and the target window isn't the currently active window
292+
if (tabGroupWindowId && lastFocusedWindow.id !== tabGroupWindowId) {
293+
await chrome.windows.remove(tabGroupWindowId);
294+
295+
toast({
296+
type: 'success',
297+
message: `Closed window with ${tabs.length} ${pluralize(tabs.length, 'tab', 'tabs')}`,
298+
});
299+
return;
300+
}
289301

290302
const tabsToClose =
291-
// Close all tabs in the group if grouping by window, and the target window isn't the currently active window
292-
groupType === 'Window' && lastFocusedWindow.id !== tabs.find(({ windowId }) => windowId)?.windowId
293-
? tabs
294-
: // Allow closing active tabs if the panel is opened in the popout
295-
// ? Otherwise, closing the active tab closes the popup
296-
OPENED_IN_POPOUT
297-
? tabs
298-
: tabs.filter(({ active }) => !active);
303+
// Allow closing active tabs if the panel is opened in the popout
304+
// ? Otherwise, closing the active tab closes the popup
305+
OPENED_IN_POPOUT ? tabs : tabs.filter(({ active }) => !active);
299306

300307
await chrome.tabs.remove(tabsToClose.map(({ id }) => id!));
301308

0 commit comments

Comments
 (0)