2012-07-29 13:35:01 +00:00
|
|
|
{- git-annex assistant alerts
|
|
|
|
-
|
2014-01-23 19:09:43 +00:00
|
|
|
- Copyright 2012-2014 Joey Hess <joey@kitenet.net>
|
2012-07-29 13:35:01 +00:00
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
2014-01-06 01:30:48 +00:00
|
|
|
{-# LANGUAGE OverloadedStrings, CPP, BangPatterns #-}
|
2012-07-29 13:35:01 +00:00
|
|
|
|
|
|
|
module Assistant.Alert where
|
|
|
|
|
2012-07-29 22:07:45 +00:00
|
|
|
import Common.Annex
|
2013-04-04 05:48:26 +00:00
|
|
|
import Assistant.Types.Alert
|
|
|
|
import Assistant.Alert.Utility
|
2012-07-29 22:07:45 +00:00
|
|
|
import qualified Remote
|
2012-08-06 19:00:46 +00:00
|
|
|
import Utility.Tense
|
2012-08-06 21:09:23 +00:00
|
|
|
import Logs.Transfer
|
2013-11-23 16:39:36 +00:00
|
|
|
import Types.Distribution
|
2014-04-08 19:23:50 +00:00
|
|
|
import Git.Types (RemoteName)
|
2012-07-29 22:07:45 +00:00
|
|
|
|
2012-08-06 19:00:46 +00:00
|
|
|
import Data.String
|
2013-04-04 05:48:26 +00:00
|
|
|
import qualified Data.Text as T
|
2013-10-27 20:42:13 +00:00
|
|
|
import qualified Control.Exception as E
|
2012-07-29 13:35:01 +00:00
|
|
|
|
2013-04-04 05:48:26 +00:00
|
|
|
#ifdef WITH_WEBAPP
|
|
|
|
import Assistant.DaemonStatus
|
|
|
|
import Assistant.WebApp.Types
|
2013-10-28 15:24:25 +00:00
|
|
|
import Assistant.WebApp (renderUrl)
|
2013-04-04 05:50:36 +00:00
|
|
|
import Yesod
|
2013-04-04 05:48:26 +00:00
|
|
|
#endif
|
2013-10-28 15:24:25 +00:00
|
|
|
import Assistant.Monad
|
|
|
|
import Assistant.Types.UrlRenderer
|
2013-04-04 05:48:26 +00:00
|
|
|
|
2013-10-10 22:02:33 +00:00
|
|
|
{- Makes a button for an alert that opens a Route.
|
|
|
|
-
|
|
|
|
- If autoclose is set, the button will close the alert it's
|
|
|
|
- attached to when clicked. -}
|
2013-04-04 05:48:26 +00:00
|
|
|
#ifdef WITH_WEBAPP
|
2013-10-10 22:02:33 +00:00
|
|
|
mkAlertButton :: Bool -> T.Text -> UrlRenderer -> Route WebApp -> Assistant AlertButton
|
|
|
|
mkAlertButton autoclose label urlrenderer route = do
|
2013-04-04 05:48:26 +00:00
|
|
|
close <- asIO1 removeAlert
|
|
|
|
url <- liftIO $ renderUrl urlrenderer route []
|
|
|
|
return $ AlertButton
|
|
|
|
{ buttonLabel = label
|
|
|
|
, buttonUrl = url
|
2013-10-10 22:02:33 +00:00
|
|
|
, buttonAction = if autoclose then Just close else Nothing
|
2013-11-23 04:54:08 +00:00
|
|
|
, buttonPrimary = True
|
2012-07-30 06:07:02 +00:00
|
|
|
}
|
2013-04-04 05:48:26 +00:00
|
|
|
#endif
|
2012-07-29 23:05:51 +00:00
|
|
|
|
2013-07-10 19:37:40 +00:00
|
|
|
renderData :: Alert -> TenseText
|
|
|
|
renderData = tenseWords . alertData
|
|
|
|
|
2012-07-29 23:41:17 +00:00
|
|
|
baseActivityAlert :: Alert
|
|
|
|
baseActivityAlert = Alert
|
2012-07-29 15:31:06 +00:00
|
|
|
{ alertClass = Activity
|
2012-07-29 23:41:17 +00:00
|
|
|
, alertHeader = Nothing
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertMessageRender = renderData
|
2012-08-06 19:41:42 +00:00
|
|
|
, alertData = []
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertCounter = 0
|
2012-07-29 15:31:06 +00:00
|
|
|
, alertBlockDisplay = False
|
2012-07-29 23:41:17 +00:00
|
|
|
, alertClosable = False
|
2012-07-29 23:05:51 +00:00
|
|
|
, alertPriority = Medium
|
2012-09-09 19:09:22 +00:00
|
|
|
, alertIcon = Just ActivityIcon
|
2012-08-02 13:03:04 +00:00
|
|
|
, alertCombiner = Nothing
|
|
|
|
, alertName = Nothing
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = []
|
2012-07-29 13:35:01 +00:00
|
|
|
}
|
2012-07-29 22:07:45 +00:00
|
|
|
|
2012-09-06 17:56:23 +00:00
|
|
|
warningAlert :: String -> String -> Alert
|
|
|
|
warningAlert name msg = Alert
|
|
|
|
{ alertClass = Warning
|
|
|
|
, alertHeader = Just $ tenseWords ["warning"]
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertMessageRender = renderData
|
2012-09-06 17:56:23 +00:00
|
|
|
, alertData = [UnTensed $ T.pack msg]
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertCounter = 0
|
2012-09-06 17:56:23 +00:00
|
|
|
, alertBlockDisplay = True
|
|
|
|
, alertClosable = True
|
|
|
|
, alertPriority = High
|
2012-09-09 19:09:22 +00:00
|
|
|
, alertIcon = Just ErrorIcon
|
2013-04-24 15:27:30 +00:00
|
|
|
, alertCombiner = Just $ dataCombiner $ \_old new -> new
|
2012-09-06 17:56:23 +00:00
|
|
|
, alertName = Just $ WarningAlert name
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = []
|
2012-09-06 17:56:23 +00:00
|
|
|
}
|
|
|
|
|
2013-11-24 18:04:03 +00:00
|
|
|
errorAlert :: String -> [AlertButton] -> Alert
|
|
|
|
errorAlert msg buttons = Alert
|
2013-10-22 20:02:52 +00:00
|
|
|
{ alertClass = Error
|
2013-10-22 20:30:23 +00:00
|
|
|
, alertHeader = Nothing
|
2013-10-22 20:02:52 +00:00
|
|
|
, alertMessageRender = renderData
|
|
|
|
, alertData = [UnTensed $ T.pack msg]
|
|
|
|
, alertCounter = 0
|
|
|
|
, alertBlockDisplay = True
|
|
|
|
, alertClosable = True
|
|
|
|
, alertPriority = Pinned
|
|
|
|
, alertIcon = Just ErrorIcon
|
|
|
|
, alertCombiner = Nothing
|
|
|
|
, alertName = Nothing
|
2013-11-24 18:04:03 +00:00
|
|
|
, alertButtons = buttons
|
2013-10-22 20:02:52 +00:00
|
|
|
}
|
|
|
|
|
2012-08-06 19:41:42 +00:00
|
|
|
activityAlert :: Maybe TenseText -> [TenseChunk] -> Alert
|
|
|
|
activityAlert header dat = baseActivityAlert
|
2012-07-29 23:41:17 +00:00
|
|
|
{ alertHeader = header
|
2012-08-06 19:41:42 +00:00
|
|
|
, alertData = dat
|
2012-07-29 23:41:17 +00:00
|
|
|
}
|
|
|
|
|
2012-07-29 22:07:45 +00:00
|
|
|
startupScanAlert :: Alert
|
2012-09-13 04:57:52 +00:00
|
|
|
startupScanAlert = activityAlert Nothing
|
2012-08-06 19:41:42 +00:00
|
|
|
[Tensed "Performing" "Performed", "startup scan"]
|
2012-07-29 22:07:45 +00:00
|
|
|
|
2013-04-10 21:52:04 +00:00
|
|
|
{- Displayed when a shutdown is occurring, so will be seen after shutdown
|
|
|
|
- has happened. -}
|
|
|
|
shutdownAlert :: Alert
|
|
|
|
shutdownAlert = warningAlert "shutdown" "git-annex has been shut down"
|
|
|
|
|
2012-08-02 18:02:35 +00:00
|
|
|
commitAlert :: Alert
|
2012-09-13 04:57:52 +00:00
|
|
|
commitAlert = activityAlert Nothing
|
2012-08-06 19:00:46 +00:00
|
|
|
[Tensed "Committing" "Committed", "changes to git"]
|
|
|
|
|
2014-04-08 19:23:50 +00:00
|
|
|
showRemotes :: [RemoteName] -> TenseChunk
|
|
|
|
showRemotes = UnTensed . T.intercalate ", " . map T.pack
|
2012-08-02 18:02:35 +00:00
|
|
|
|
2012-08-06 21:09:23 +00:00
|
|
|
syncAlert :: [Remote] -> Alert
|
2014-04-08 19:23:50 +00:00
|
|
|
syncAlert = syncAlert' . map Remote.name
|
|
|
|
|
|
|
|
syncAlert' :: [RemoteName] -> Alert
|
|
|
|
syncAlert' rs = baseActivityAlert
|
2013-03-18 20:19:42 +00:00
|
|
|
{ alertName = Just SyncAlert
|
|
|
|
, alertHeader = Just $ tenseWords
|
2012-08-06 21:09:23 +00:00
|
|
|
[Tensed "Syncing" "Synced", "with", showRemotes rs]
|
2012-07-29 23:05:51 +00:00
|
|
|
, alertPriority = Low
|
2013-04-09 02:54:02 +00:00
|
|
|
, alertIcon = Just SyncIcon
|
2012-12-13 04:45:27 +00:00
|
|
|
}
|
2012-07-29 22:07:45 +00:00
|
|
|
|
2013-03-18 20:19:42 +00:00
|
|
|
syncResultAlert :: [Remote] -> [Remote] -> Alert
|
2014-04-08 19:23:50 +00:00
|
|
|
syncResultAlert succeeded failed = syncResultAlert'
|
|
|
|
(map Remote.name succeeded)
|
|
|
|
(map Remote.name failed)
|
|
|
|
|
|
|
|
syncResultAlert' :: [RemoteName] -> [RemoteName] -> Alert
|
|
|
|
syncResultAlert' succeeded failed = makeAlertFiller (not $ null succeeded) $
|
2013-03-18 20:19:42 +00:00
|
|
|
baseActivityAlert
|
|
|
|
{ alertName = Just SyncAlert
|
|
|
|
, alertHeader = Just $ tenseWords msg
|
|
|
|
}
|
|
|
|
where
|
2014-10-09 18:53:13 +00:00
|
|
|
msg
|
2013-03-18 20:19:42 +00:00
|
|
|
| null succeeded = ["Failed to sync with", showRemotes failed]
|
|
|
|
| null failed = ["Synced with", showRemotes succeeded]
|
|
|
|
| otherwise =
|
|
|
|
[ "Synced with", showRemotes succeeded
|
|
|
|
, "but not with", showRemotes failed
|
|
|
|
]
|
2012-07-29 22:07:45 +00:00
|
|
|
|
|
|
|
sanityCheckAlert :: Alert
|
2012-08-06 19:00:46 +00:00
|
|
|
sanityCheckAlert = activityAlert
|
|
|
|
(Just $ tenseWords [Tensed "Running" "Ran", "daily sanity check"])
|
2012-08-06 19:41:42 +00:00
|
|
|
["to make sure everything is ok."]
|
2012-07-29 22:07:45 +00:00
|
|
|
|
|
|
|
sanityCheckFixAlert :: String -> Alert
|
|
|
|
sanityCheckFixAlert msg = Alert
|
|
|
|
{ alertClass = Warning
|
2012-08-06 19:00:46 +00:00
|
|
|
, alertHeader = Just $ tenseWords ["Fixed a problem"]
|
2012-08-06 19:41:42 +00:00
|
|
|
, alertMessageRender = render
|
|
|
|
, alertData = [UnTensed $ T.pack msg]
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertCounter = 0
|
2012-07-29 22:07:45 +00:00
|
|
|
, alertBlockDisplay = True
|
2012-07-29 23:05:51 +00:00
|
|
|
, alertPriority = High
|
2012-07-29 23:41:17 +00:00
|
|
|
, alertClosable = True
|
2012-09-09 19:09:22 +00:00
|
|
|
, alertIcon = Just ErrorIcon
|
2012-08-02 13:03:04 +00:00
|
|
|
, alertName = Just SanityCheckFixAlert
|
2012-08-06 19:41:42 +00:00
|
|
|
, alertCombiner = Just $ dataCombiner (++)
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = []
|
2012-08-02 13:03:04 +00:00
|
|
|
}
|
2012-10-31 06:34:03 +00:00
|
|
|
where
|
2013-07-10 19:37:40 +00:00
|
|
|
render alert = tenseWords $ alerthead : alertData alert ++ [alertfoot]
|
2012-10-31 06:34:03 +00:00
|
|
|
alerthead = "The daily sanity check found and fixed a problem:"
|
|
|
|
alertfoot = "If these problems persist, consider filing a bug report."
|
2012-08-02 13:03:04 +00:00
|
|
|
|
2013-10-29 20:48:06 +00:00
|
|
|
fsckingAlert :: AlertButton -> Maybe Remote -> Alert
|
|
|
|
fsckingAlert button mr = baseActivityAlert
|
|
|
|
{ alertData = case mr of
|
2013-10-11 20:03:18 +00:00
|
|
|
Nothing -> [ UnTensed $ T.pack $ "Consistency check in progress" ]
|
2013-10-29 20:48:06 +00:00
|
|
|
Just r -> [ UnTensed $ T.pack $ "Consistency check of " ++ Remote.name r ++ " in progress"]
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = [button]
|
2013-10-10 22:02:33 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 20:48:06 +00:00
|
|
|
showFscking :: UrlRenderer -> Maybe Remote -> IO (Either E.SomeException a) -> Assistant a
|
|
|
|
showFscking urlrenderer mr a = do
|
2013-10-27 20:42:13 +00:00
|
|
|
#ifdef WITH_WEBAPP
|
|
|
|
button <- mkAlertButton False (T.pack "Configure") urlrenderer ConfigFsckR
|
2013-10-29 20:48:06 +00:00
|
|
|
r <- alertDuring (fsckingAlert button mr) $
|
2013-10-27 20:42:13 +00:00
|
|
|
liftIO a
|
|
|
|
#else
|
2013-10-28 15:24:25 +00:00
|
|
|
r <- liftIO a
|
2013-10-27 20:42:13 +00:00
|
|
|
#endif
|
2013-10-28 15:24:25 +00:00
|
|
|
either (liftIO . E.throwIO) return r
|
2013-10-27 20:42:13 +00:00
|
|
|
|
2013-10-29 20:48:06 +00:00
|
|
|
notFsckedNudge :: UrlRenderer -> Maybe Remote -> Assistant ()
|
|
|
|
#ifdef WITH_WEBAPP
|
|
|
|
notFsckedNudge urlrenderer mr = do
|
|
|
|
button <- mkAlertButton True (T.pack "Configure") urlrenderer ConfigFsckR
|
|
|
|
void $ addAlert (notFsckedAlert mr button)
|
|
|
|
#else
|
2013-11-02 17:38:44 +00:00
|
|
|
notFsckedNudge _ _ = noop
|
2013-10-29 20:48:06 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
notFsckedAlert :: Maybe Remote -> AlertButton -> Alert
|
|
|
|
notFsckedAlert mr button = Alert
|
|
|
|
{ alertHeader = Just $ fromString $ concat
|
|
|
|
[ "You should enable consistency checking to protect your data"
|
|
|
|
, maybe "" (\r -> " in " ++ Remote.name r) mr
|
|
|
|
, "."
|
|
|
|
]
|
|
|
|
, alertIcon = Just InfoIcon
|
|
|
|
, alertPriority = High
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = [button]
|
2013-10-29 20:48:06 +00:00
|
|
|
, alertClosable = True
|
|
|
|
, alertClass = Message
|
|
|
|
, alertMessageRender = renderData
|
|
|
|
, alertCounter = 0
|
|
|
|
, alertBlockDisplay = True
|
|
|
|
, alertName = Just NotFsckedAlert
|
|
|
|
, alertCombiner = Just $ dataCombiner $ \_old new -> new
|
|
|
|
, alertData = []
|
|
|
|
}
|
|
|
|
|
global webapp redirects, to finish upgrades
When an automatic upgrade completes, or when the user clicks on the upgrade
button in one webapp, but also has it open in another browser window/tab,
we have a problem: The current web server is going to stop running in
minutes, but there is no way to send a redirect to the web browser to the
new url.
To solve this, used long polling, so the webapp is always listening for
urls it should redirect to. This allows globally redirecting every open
webapp. Works great! Tested with 2 web browsers with 2 tabs each.
May be useful for other purposes later too, dunno.
The overhead is 2 http requests per page load in the webapp. Due to yesod's
speed, this does not seem to noticibly delay it. Only 1 of the requests
could possibly block the page load, the other is async.
2013-11-23 18:47:38 +00:00
|
|
|
baseUpgradeAlert :: [AlertButton] -> TenseText -> Alert
|
|
|
|
baseUpgradeAlert buttons message = Alert
|
2013-11-23 16:39:36 +00:00
|
|
|
{ alertHeader = Just message
|
2013-11-21 21:49:56 +00:00
|
|
|
, alertIcon = Just UpgradeIcon
|
2013-11-23 16:39:36 +00:00
|
|
|
, alertPriority = High
|
global webapp redirects, to finish upgrades
When an automatic upgrade completes, or when the user clicks on the upgrade
button in one webapp, but also has it open in another browser window/tab,
we have a problem: The current web server is going to stop running in
minutes, but there is no way to send a redirect to the web browser to the
new url.
To solve this, used long polling, so the webapp is always listening for
urls it should redirect to. This allows globally redirecting every open
webapp. Works great! Tested with 2 web browsers with 2 tabs each.
May be useful for other purposes later too, dunno.
The overhead is 2 http requests per page load in the webapp. Due to yesod's
speed, this does not seem to noticibly delay it. Only 1 of the requests
could possibly block the page load, the other is async.
2013-11-23 18:47:38 +00:00
|
|
|
, alertButtons = buttons
|
2013-11-21 21:49:56 +00:00
|
|
|
, alertClosable = True
|
|
|
|
, alertClass = Message
|
|
|
|
, alertMessageRender = renderData
|
|
|
|
, alertCounter = 0
|
|
|
|
, alertBlockDisplay = True
|
2013-11-23 16:39:36 +00:00
|
|
|
, alertName = Just UpgradeAlert
|
|
|
|
, alertCombiner = Just $ fullCombiner $ \new _old -> new
|
2013-11-21 21:49:56 +00:00
|
|
|
, alertData = []
|
|
|
|
}
|
|
|
|
|
2013-11-24 17:28:34 +00:00
|
|
|
canUpgradeAlert :: AlertPriority -> GitAnnexVersion -> AlertButton -> Alert
|
|
|
|
canUpgradeAlert priority version button =
|
global webapp redirects, to finish upgrades
When an automatic upgrade completes, or when the user clicks on the upgrade
button in one webapp, but also has it open in another browser window/tab,
we have a problem: The current web server is going to stop running in
minutes, but there is no way to send a redirect to the web browser to the
new url.
To solve this, used long polling, so the webapp is always listening for
urls it should redirect to. This allows globally redirecting every open
webapp. Works great! Tested with 2 web browsers with 2 tabs each.
May be useful for other purposes later too, dunno.
The overhead is 2 http requests per page load in the webapp. Due to yesod's
speed, this does not seem to noticibly delay it. Only 1 of the requests
could possibly block the page load, the other is async.
2013-11-23 18:47:38 +00:00
|
|
|
(baseUpgradeAlert [button] $ fromString msg)
|
2013-11-24 17:28:34 +00:00
|
|
|
{ alertPriority = priority
|
|
|
|
, alertData = [fromString $ " (version " ++ version ++ ")"]
|
|
|
|
}
|
2013-11-23 16:39:36 +00:00
|
|
|
where
|
|
|
|
msg = if priority >= High
|
|
|
|
then "An important upgrade of git-annex is available!"
|
|
|
|
else "An upgrade of git-annex is available."
|
|
|
|
|
|
|
|
upgradeReadyAlert :: AlertButton -> Alert
|
global webapp redirects, to finish upgrades
When an automatic upgrade completes, or when the user clicks on the upgrade
button in one webapp, but also has it open in another browser window/tab,
we have a problem: The current web server is going to stop running in
minutes, but there is no way to send a redirect to the web browser to the
new url.
To solve this, used long polling, so the webapp is always listening for
urls it should redirect to. This allows globally redirecting every open
webapp. Works great! Tested with 2 web browsers with 2 tabs each.
May be useful for other purposes later too, dunno.
The overhead is 2 http requests per page load in the webapp. Due to yesod's
speed, this does not seem to noticibly delay it. Only 1 of the requests
could possibly block the page load, the other is async.
2013-11-23 18:47:38 +00:00
|
|
|
upgradeReadyAlert button = baseUpgradeAlert [button] $
|
2013-11-23 16:39:36 +00:00
|
|
|
fromString "A new version of git-annex has been installed."
|
2013-11-23 03:12:06 +00:00
|
|
|
|
|
|
|
upgradingAlert :: Alert
|
2013-11-23 16:39:36 +00:00
|
|
|
upgradingAlert = activityAlert Nothing [ fromString "Upgrading git-annex" ]
|
|
|
|
|
global webapp redirects, to finish upgrades
When an automatic upgrade completes, or when the user clicks on the upgrade
button in one webapp, but also has it open in another browser window/tab,
we have a problem: The current web server is going to stop running in
minutes, but there is no way to send a redirect to the web browser to the
new url.
To solve this, used long polling, so the webapp is always listening for
urls it should redirect to. This allows globally redirecting every open
webapp. Works great! Tested with 2 web browsers with 2 tabs each.
May be useful for other purposes later too, dunno.
The overhead is 2 http requests per page load in the webapp. Due to yesod's
speed, this does not seem to noticibly delay it. Only 1 of the requests
could possibly block the page load, the other is async.
2013-11-23 18:47:38 +00:00
|
|
|
upgradeFinishedAlert :: Maybe AlertButton -> GitAnnexVersion -> Alert
|
2013-11-23 16:39:36 +00:00
|
|
|
upgradeFinishedAlert button version =
|
2014-02-01 21:14:38 +00:00
|
|
|
baseUpgradeAlert (maybeToList button) $ fromString $
|
2013-11-23 16:39:36 +00:00
|
|
|
"Finished upgrading git-annex to version " ++ version
|
2013-11-23 03:12:06 +00:00
|
|
|
|
2013-11-24 18:04:03 +00:00
|
|
|
upgradeFailedAlert :: String -> Alert
|
|
|
|
upgradeFailedAlert msg = (errorAlert msg [])
|
|
|
|
{ alertHeader = Just $ fromString "Upgrade failed." }
|
|
|
|
|
assistant unused file handling
Make sanity checker run git annex unused daily, and queue up transfers
of unused files to any remotes that will have them. The transfer retrying
code works for us here, so eg when a backup disk remote is plugged in,
any transfers to it are done. Once the unused files reach a remote,
they'll be removed locally as unwanted.
If the setup does not cause unused files to go to a remote, they'll pile
up, and the sanity checker detects this using some heuristics that are
pretty good -- 1000 unused files, or 10% of disk used by unused files,
or more disk wasted by unused files than is left free. Once it detects
this, it pops up an alert in the webapp, with a button to take action.
TODO: Webapp UI to configure this, and also the ability to launch an
immediate cleanup of all unused files.
This commit was sponsored by Simon Michael.
2014-01-23 02:48:56 +00:00
|
|
|
unusedFilesAlert :: [AlertButton] -> String -> Alert
|
|
|
|
unusedFilesAlert buttons message = Alert
|
|
|
|
{ alertHeader = Just $ fromString $ unwords
|
|
|
|
[ "Old and deleted files are piling up --"
|
|
|
|
, message
|
|
|
|
]
|
|
|
|
, alertIcon = Just InfoIcon
|
|
|
|
, alertPriority = High
|
|
|
|
, alertButtons = buttons
|
|
|
|
, alertClosable = True
|
|
|
|
, alertClass = Message
|
|
|
|
, alertMessageRender = renderData
|
|
|
|
, alertCounter = 0
|
|
|
|
, alertBlockDisplay = True
|
|
|
|
, alertName = Just UnusedFilesAlert
|
|
|
|
, alertCombiner = Just $ fullCombiner $ \new _old -> new
|
|
|
|
, alertData = []
|
|
|
|
}
|
|
|
|
|
2013-11-24 18:04:03 +00:00
|
|
|
brokenRepositoryAlert :: [AlertButton] -> Alert
|
2013-10-22 20:30:23 +00:00
|
|
|
brokenRepositoryAlert = errorAlert "Serious problems have been detected with your repository. This needs your immediate attention!"
|
2013-10-22 20:02:52 +00:00
|
|
|
|
2013-10-27 19:38:59 +00:00
|
|
|
repairingAlert :: String -> Alert
|
|
|
|
repairingAlert repodesc = activityAlert Nothing
|
|
|
|
[ Tensed "Attempting to repair" "Repaired"
|
|
|
|
, UnTensed $ T.pack repodesc
|
|
|
|
]
|
|
|
|
|
2012-09-10 21:53:51 +00:00
|
|
|
pairingAlert :: AlertButton -> Alert
|
|
|
|
pairingAlert button = baseActivityAlert
|
|
|
|
{ alertData = [ UnTensed "Pairing in progress" ]
|
2012-09-09 20:24:34 +00:00
|
|
|
, alertPriority = High
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = [button]
|
2012-09-09 20:24:34 +00:00
|
|
|
}
|
|
|
|
|
2012-09-11 19:06:29 +00:00
|
|
|
pairRequestReceivedAlert :: String -> AlertButton -> Alert
|
2012-11-03 21:34:19 +00:00
|
|
|
pairRequestReceivedAlert who button = Alert
|
2012-09-11 20:11:28 +00:00
|
|
|
{ alertClass = Message
|
|
|
|
, alertHeader = Nothing
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertMessageRender = renderData
|
2012-11-03 21:34:19 +00:00
|
|
|
, alertData = [UnTensed $ T.pack $ who ++ " is sending a pair request."]
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertCounter = 0
|
2012-09-11 20:11:28 +00:00
|
|
|
, alertBlockDisplay = False
|
2012-09-08 19:07:44 +00:00
|
|
|
, alertPriority = High
|
|
|
|
, alertClosable = True
|
2012-09-09 19:09:22 +00:00
|
|
|
, alertIcon = Just InfoIcon
|
2012-11-03 21:34:19 +00:00
|
|
|
, alertName = Just $ PairAlert who
|
2012-09-11 19:06:29 +00:00
|
|
|
, alertCombiner = Just $ dataCombiner $ \_old new -> new
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = [button]
|
2012-09-11 19:06:29 +00:00
|
|
|
}
|
|
|
|
|
2012-09-13 04:57:52 +00:00
|
|
|
pairRequestAcknowledgedAlert :: String -> Maybe AlertButton -> Alert
|
2012-11-03 21:34:19 +00:00
|
|
|
pairRequestAcknowledgedAlert who button = baseActivityAlert
|
2012-11-05 21:43:17 +00:00
|
|
|
{ alertData = ["Pairing with", UnTensed (T.pack who), Tensed "in progress" "complete"]
|
2012-09-11 19:06:29 +00:00
|
|
|
, alertPriority = High
|
2013-03-05 20:23:56 +00:00
|
|
|
, alertName = Just $ PairAlert who
|
2012-09-11 19:06:29 +00:00
|
|
|
, alertCombiner = Just $ dataCombiner $ \_old new -> new
|
2014-02-01 21:14:38 +00:00
|
|
|
, alertButtons = maybeToList button
|
2012-09-08 19:07:44 +00:00
|
|
|
}
|
|
|
|
|
2014-04-09 20:27:24 +00:00
|
|
|
connectionNeededAlert :: AlertButton -> Alert
|
|
|
|
connectionNeededAlert button = Alert
|
2012-11-11 00:52:46 +00:00
|
|
|
{ alertHeader = Just "Share with friends, and keep your devices in sync across the cloud."
|
2014-04-09 20:27:24 +00:00
|
|
|
, alertIcon = Just ConnectionIcon
|
2012-10-27 16:25:29 +00:00
|
|
|
, alertPriority = High
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = [button]
|
2012-10-27 16:25:29 +00:00
|
|
|
, alertClosable = True
|
|
|
|
, alertClass = Message
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertMessageRender = renderData
|
|
|
|
, alertCounter = 0
|
2012-10-27 16:25:29 +00:00
|
|
|
, alertBlockDisplay = True
|
2014-04-09 20:27:24 +00:00
|
|
|
, alertName = Just ConnectionNeededAlert
|
2012-10-27 16:25:29 +00:00
|
|
|
, alertCombiner = Just $ dataCombiner $ \_old new -> new
|
|
|
|
, alertData = []
|
|
|
|
}
|
|
|
|
|
2013-03-15 21:52:41 +00:00
|
|
|
cloudRepoNeededAlert :: Maybe String -> AlertButton -> Alert
|
|
|
|
cloudRepoNeededAlert friendname button = Alert
|
|
|
|
{ alertHeader = Just $ fromString $ unwords
|
|
|
|
[ "Unable to download files from"
|
|
|
|
, (fromMaybe "your other devices" friendname) ++ "."
|
|
|
|
]
|
|
|
|
, alertIcon = Just ErrorIcon
|
|
|
|
, alertPriority = High
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = [button]
|
2013-03-15 21:52:41 +00:00
|
|
|
, alertClosable = True
|
|
|
|
, alertClass = Message
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertMessageRender = renderData
|
|
|
|
, alertCounter = 0
|
2013-03-15 21:52:41 +00:00
|
|
|
, alertBlockDisplay = True
|
|
|
|
, alertName = Just $ CloudRepoNeededAlert
|
|
|
|
, alertCombiner = Just $ dataCombiner $ \_old new -> new
|
|
|
|
, alertData = []
|
|
|
|
}
|
|
|
|
|
2013-04-03 21:01:40 +00:00
|
|
|
remoteRemovalAlert :: String -> AlertButton -> Alert
|
|
|
|
remoteRemovalAlert desc button = Alert
|
|
|
|
{ alertHeader = Just $ fromString $
|
|
|
|
"The repository \"" ++ desc ++
|
|
|
|
"\" has been emptied, and can now be removed."
|
|
|
|
, alertIcon = Just InfoIcon
|
|
|
|
, alertPriority = High
|
2013-11-23 04:54:08 +00:00
|
|
|
, alertButtons = [button]
|
2013-04-03 21:01:40 +00:00
|
|
|
, alertClosable = True
|
|
|
|
, alertClass = Message
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertMessageRender = renderData
|
|
|
|
, alertCounter = 0
|
2013-04-03 21:01:40 +00:00
|
|
|
, alertBlockDisplay = True
|
|
|
|
, alertName = Just $ RemoteRemovalAlert desc
|
|
|
|
, alertCombiner = Just $ dataCombiner $ \_old new -> new
|
|
|
|
, alertData = []
|
|
|
|
}
|
|
|
|
|
2013-07-10 19:37:40 +00:00
|
|
|
{- Show a message that relates to a list of files.
|
|
|
|
-
|
|
|
|
- The most recent several files are shown, and a count of any others. -}
|
|
|
|
fileAlert :: TenseChunk -> [FilePath] -> Alert
|
|
|
|
fileAlert msg files = (activityAlert Nothing shortfiles)
|
2012-08-06 21:09:23 +00:00
|
|
|
{ alertName = Just $ FileAlert msg
|
2013-07-10 19:37:40 +00:00
|
|
|
, alertMessageRender = renderer
|
|
|
|
, alertCounter = counter
|
|
|
|
, alertCombiner = Just $ fullCombiner combiner
|
2012-07-29 22:07:45 +00:00
|
|
|
}
|
2012-10-31 06:34:03 +00:00
|
|
|
where
|
2013-07-10 19:37:40 +00:00
|
|
|
maxfilesshown = 10
|
|
|
|
|
2014-01-06 01:30:48 +00:00
|
|
|
(!somefiles, !counter) = splitcounter (dedupadjacent files)
|
|
|
|
!shortfiles = map (fromString . shortFile . takeFileName) somefiles
|
2013-07-10 19:37:40 +00:00
|
|
|
|
|
|
|
renderer alert = tenseWords $ msg : alertData alert ++ showcounter
|
|
|
|
where
|
|
|
|
showcounter = case alertCounter alert of
|
|
|
|
0 -> []
|
|
|
|
_ -> [fromString $ "and " ++ show (alertCounter alert) ++ " other files"]
|
2012-08-06 19:41:42 +00:00
|
|
|
|
2013-07-10 19:37:40 +00:00
|
|
|
dedupadjacent (x:y:rest)
|
|
|
|
| x == y = dedupadjacent (y:rest)
|
|
|
|
| otherwise = x : dedupadjacent (y:rest)
|
|
|
|
dedupadjacent (x:[]) = [x]
|
|
|
|
dedupadjacent [] = []
|
|
|
|
|
|
|
|
{- Note that this ensures the counter is never 1; no need to say
|
|
|
|
- "1 file" when the filename could be shown. -}
|
|
|
|
splitcounter l
|
|
|
|
| length l <= maxfilesshown = (l, 0)
|
|
|
|
| otherwise =
|
|
|
|
let (keep, rest) = splitAt (maxfilesshown - 1) l
|
|
|
|
in (keep, length rest)
|
|
|
|
|
|
|
|
combiner new old =
|
2014-01-06 01:30:48 +00:00
|
|
|
let (!fs, n) = splitcounter $
|
2013-07-10 19:37:40 +00:00
|
|
|
dedupadjacent $ alertData new ++ alertData old
|
2014-01-06 01:30:48 +00:00
|
|
|
!cnt = n + alertCounter new + alertCounter old
|
2013-07-10 19:37:40 +00:00
|
|
|
in old
|
|
|
|
{ alertData = fs
|
|
|
|
, alertCounter = cnt
|
|
|
|
}
|
|
|
|
|
|
|
|
addFileAlert :: [FilePath] -> Alert
|
2012-08-06 21:09:23 +00:00
|
|
|
addFileAlert = fileAlert (Tensed "Adding" "Added")
|
|
|
|
|
|
|
|
{- This is only used as a success alert after a transfer, not during it. -}
|
2012-08-27 17:52:48 +00:00
|
|
|
transferFileAlert :: Direction -> Bool -> FilePath -> Alert
|
2013-07-10 19:37:40 +00:00
|
|
|
transferFileAlert direction True file
|
|
|
|
| direction == Upload = fileAlert "Uploaded" [file]
|
|
|
|
| otherwise = fileAlert "Downloaded" [file]
|
|
|
|
transferFileAlert direction False file
|
|
|
|
| direction == Upload = fileAlert "Upload failed" [file]
|
|
|
|
| otherwise = fileAlert "Download failed" [file]
|
2012-08-06 21:09:23 +00:00
|
|
|
|
2012-08-06 19:41:42 +00:00
|
|
|
dataCombiner :: ([TenseChunk] -> [TenseChunk] -> [TenseChunk]) -> AlertCombiner
|
2013-07-10 19:37:40 +00:00
|
|
|
dataCombiner combiner = fullCombiner $
|
|
|
|
\new old -> old { alertData = alertData new `combiner` alertData old }
|
|
|
|
|
|
|
|
fullCombiner :: (Alert -> Alert -> Alert) -> AlertCombiner
|
|
|
|
fullCombiner combiner new old
|
2012-08-06 19:41:42 +00:00
|
|
|
| alertClass new /= alertClass old = Nothing
|
|
|
|
| alertName new == alertName old =
|
2013-07-10 19:37:40 +00:00
|
|
|
Just $! new `combiner` old
|
2012-08-06 19:41:42 +00:00
|
|
|
| otherwise = Nothing
|
2012-08-02 17:47:26 +00:00
|
|
|
|
|
|
|
shortFile :: FilePath -> String
|
|
|
|
shortFile f
|
|
|
|
| len < maxlen = f
|
|
|
|
| otherwise = take half f ++ ".." ++ drop (len - half) f
|
2012-10-31 06:34:03 +00:00
|
|
|
where
|
|
|
|
len = length f
|
|
|
|
maxlen = 20
|
|
|
|
half = (maxlen - 2) `div` 2
|
2012-08-02 17:47:26 +00:00
|
|
|
|